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);