经过几个日日夜夜间断的努力,终于搞明白所谓的KMP算法了。
首先解释下KMP算法:它的目的是为了提取字串,解决了暴力(Brute-Force)算法冗余的指针回溯,从而提高了算法性能。其主要使用《算法导论》中的next数组,加速匹配。
举个栗子:
母串:abcabcabcabcabcd
子串:abcabcd
初始代码写手大概回使用如下的匹配方式:
do
abcabcabcabcabcd
abcabcd(d!=a ==> 母串指针+1)
abcabcabcabcabcd
a(a!=b ==>母串指针+1)
loop
可是对比字串、母串的结构发现,最优的对比方式应该是:
do
abcabcabcabcabcd
abcabcd(a!=d ==> 母串指针+3)
abcabcabcabcabcd
abcabcd(a!=d ==> 母串指针+3)
abcabcabcabcabcd
abcabcd(a!=d ==> 母串指针+3)
loop
那么疑问来了,经过我们观察,知道该字串可自增3来提高算法性能。一个未知的字串怎么计算呢?即:如何跳过字串重复部分来进行比较。
《算法导论》中指出:可使用next数组解决该问题。
先上代码:
void make_next(const char *pattern, int *next) {
int q, k;
int m = strlen(pattern);
next[0] = 0; //
for (q = 1,k = 0;q < m; q ++) {
while (k > 0 && pattern[q] != pattern[k]) {
k = (next[k-1]-1)+1;
}
if (pattern[q] == pattern[k]) {
k ++;
}
next[q] = k;
}
}
该算法原理:
在pattern中找到与其前缀相同的最大字符串的最后一个字符的位置,将之记录。在接下来寻找pattern位置时候,从前往后遍历,当发现pattern与text不能够匹配的时候,直接回溯pattern与当前匹配位置相匹配的前缀位置进行比较。
int kmp(const char *text, const char *pattern, int *next) {
int n = strlen(text);
int m = strlen(pattern);
make_next(pattern, next);
int i, q;
for (i = 0, q = 0;i < n;i ++) { //i --> text, q --> pattern
while (q > 0 && pattern[q] != text[i]) {
q = (next[q-1]-1) + 1;
}
if (pattern[q] == text[i]) {
q ++;
}
// q == m --->
if (q == m) {
return i-q+1;
}
}
return -1;
}
解释
k = (next[k-1]-1) + 1;
q = (next[q-1]-1) + 1;
next[]-1是因为存储该位置时候先进行了k++,即记录的是匹配位置的下一个字符坐标。 next[] - 1 是为了找到最后匹配位置坐标。 (next[]-1) + 1 为了获取下一个字符坐标,为了与当前text做比较。
有兴趣可以加qq群: