今天看KMP算法,对于next函数的理解一直有些困难,找了好久网上的资料,老师的PPT,最后终于找到了一个我可以理解的说法。
有关KMP算法,因为模式匹配(找字符串子串位置)使用暴力回溯很浪费时间,所以诞生了KMP算法。KMP算法引用了一个next函数,计算目标字符串的next参数,这里有两种标记方法,我比较倾向于以-1开头的,因为可以把它看做位移,便于我的理解;另一种是0开头的。
KMP:
(1)next算法:使用递推的方法,next[1]=0;next[j]=k;next[j+1]=?
这里有两种情况,一种是p[j] = p[k] (这里的j和k都是下一循环中的),这个就直接让next[j+1]=k+1就可以了。
另外一种情况,是我弄了好久才搞懂的地方,就是这一句“k=next[k]”,课本上就这么一句话,简直horrible!不过,幸运的是我在网上找了一个和我有同样烦恼的前辈,然后把他的文章附在这里:KMP
他里面讲的主要是,如果匹配失败了,需要回退,回退到这个前面n个位置,这里的n是返回失配位之前最长公共前后缀对应的前缀后一位的地方。这句话,是这个大佬说的,我觉得比较容易理解理解。
(2)KMP算法
类似我之前说的,把next看做位移,就很好理解了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
void cal_next(char *str, int *next, int len)
{
next[0] = -1;
int k = -1;
for (int q = 1; q <= len-1; q++)
{
while (k > -1 && str[k + 1] != str[q])
{
k = next[k];
}
if (str[k + 1] == str[q])
{
k = k + 1;
}
next[q] = k;
}
}
int KMP(char *str, int slen, char *ptr, int plen)
{
int *next = new int[plen];
cal_next(ptr, next, plen);
int k = -1;
for (int i = 0; i < slen; i++)
{
while (k >-1&& ptr[k + 1] != str[i])
k = next[k];
if (ptr[k + 1] == str[i])
k = k + 1;
if (k == plen-1)
{
//cout << "在位置" << i-plen+1<< endl;
//k = -1;//重新初始化,寻找下一个
//i = i - plen + 1;//i定位到该位置,外层for循环i++可以继续找下一个(这里默认存在两个匹配字符串可以部分重叠)
return i-plen+1;
}
}
return -1;
}
int main()
{
char *str = "bacbababadababacambabacaddababacasdsd";
char *ptr = "ababaca";
int a = KMP(str, 36, ptr, 7);
printf("%d",a);
return 0;
}
代码摘抄来自:link