String match algorithm인 KMP algorithm은 text에서 뛰어넘을 수 있는 만큼 뛰어넘으면서 string이 match되는지 확인한다. 여기서 얼마나 뛰어넘을 수 있는지에 대한 정보를 얻기 위해 Failure function이라는 함수를 이용한다.
Failure function은 찾으려는 string의 각 위치마다 공통인 prefix와 suffix의 최대 길이를 찾아 저장해둔다. 예를 들면, “ababa” 는 { 0, 0, 1, 2, 3 } 과 같이 저장한다. 구현 시에는 -1 해서 저장한다. 예에서의 값은 { -1, -1, 0, 1, 2 } 이 된다.
void get_failure_function(char str[], int len, int failure[]) { int i; failure[0] = -1; for (i = 1; i < len; i++) { int f = failure[i - 1]; while (1) { if (str[i] == str[f + 1]) { failure[i] = f + 1; break; } if (f <= -1) { failure[i] = -1; break; } f = failure[f]; } } }
찾을 때는, 위에서 계산한 값을 이용해 string을 찾는 도중에 틀리면 바로 이전까지 맞았던 부분의 failure function value를 찾아 이전까지 같은 부분을 건너뛰고 다음 부분부터 비교한다. 예를 들어, 위의 string을 찾으려고 할 때, text가 “ababcababa” 라면, string의 처음부터 비교하다가 5번째에서 틀리면, 4번째까지는 맞으므로, 4번째의 failure function value를 찾으면 2이다. 이는 앞의 2글자는 맞다는 말이므로, text에서 4-2 만큼을 건너뛴 후, string의 3번째부터 다시 비교한다. string의 3번째도 틀리므로, 2번째의 failure function value를 찾으면 0인데, 이는 0만큼 맞다는 말(=하나도 안맞다는 말)이므로 text를 2-0 만큼 건너뛴 후, string의 1번째와 다시 비교한다. string의 첫번째와 다르면 1만큼만 이동하고, 다시 1번째와 비교한다. 맞았을 때도 맞은 부분만큼을 이용해서 건너뛴다(5번째에서 맞았으므로, 3글자가 맞고, 5-3만큼 이동한 후, 4번째부터 비교).
아래 구현은 match되는 개수를 return하도록 하였다.
int get_matched_count(char text[], int text_len, char string[], int str_len, int failure[]) { int count = 0; int t = 0; int s = 0; while (t < text_len - str_len + 1) { while (s < str_len) { if (text[t + s] != string[s]) break; s++; } if (s == str_len) { // MATCH! count++; } if (s < 1) { t++; s = 0; } else { t += s - 1 - failure[s - 1]; s = failure[s - 1] + 1; } } return count; }
앞 부분과 공통 부분이 없는 string인 경우, 더 빨리 건너뛴다. 예를 들면, “abababababc” 에서 “ababc”({ 0, 0, 1, 2, 0 })를 찾는 경우, 5번째에서 틀리면, 앞에서부터 0글자가 같으므로, 4-0만큼 이동한 후, 1번째부터 다시 비교한다.
위 두 구현 함수의 사용 부분은 다음과 같이 될 것이다.
int T, S; int count; int *failure_function = NULL; char *text = "ababcababababababababa"; char *string = "ababa"; T = strlen(text); S = strlen(string); failure_function = (int *)malloc(sizeof(int) * S); // skip the check of the memory allocation get_failure_function(string, S, failure_function); count = get_matched_count(text, T, string, S, failure_function); printf("%d matched\n", count);