什么是KMP算法?
模式串 char[] pat = new char[M]; 文本串 char[] txt = new char[N];
假设当前正在比较的字符是txt[i]和pat[j], 即pat[0, j - 1]已经匹配成功。txt[i]后面的字符处于未知状态。
pat[j]之前的子字符串的最长相同前缀后缀的长度为k,即pat[0, k -1] 与pat[j – k, j – 1]依次在对应位上相等。
如果匹配失败,则下一次比较的字符对是txt[i]和pat[k]。
可以看出,向右移动了j - k位。如果按照暴力查找法,则只会移动1位。
next[]数组的求解?
下面分析一下如果已知next[j]=k,如何推导出next[j+1]。
求next[j+1]就是求字符串pat[0,j]的最长前缀后缀的长度。
1. 如果pat[k]==p[j],则next[j+1] = next[j] + 1 = k + 1;
1. 如果pat[k]≠p[j],暂时还不能确定,需进一步判断。记next[k]的值为m,若pat[m] == pat[j],则next[j+1] = m + 1;否则继续递归前缀索引k = next[k],而后重复此过程,直到m=-1,表示匹配失败,next[j+1] = 0。
记,如下图。
注:
若next[k]=m, 则:
pat[0,m-1] = pat[k-m,k-1];
再根据next[j]=k,则
pat[k-m,k-1] = pat[k-m + (j-k),k-1+(j-k)] = pat[j-m,j-1],
从而得出结论:
pat最前面的m个字符和索引j之前的m个字符依次相等。
附求Next数组的代码:
void GetNext(String p,int[] next)
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
/*循环起始,k的值是子串a[0,j-1]的最大前缀后缀长度,即next[j];为了计算a[0,j]的最大前缀后缀长,需要先比较a[j]和a[k],如果:
1.相等,则next[j+1] = k++,求解完毕。同时j++,准备下一次循环;
2.不相等,则让p[next[k]]再和p[j]比较:若
2.1 相等,则next[j+1]就是next[k]+1,求解完毕。同时j++,准备下一次循环。
2.2 不相等,则next[k]再往下迭代,
。。。
直到碰到next的值是-1时停止,表示匹配失败。
*/
if (k == -1 || p.charAt(j) == p.charAt(k))
{
++k;
++j;
if(p.charAt[j] != p.charAt(k)){
next[j] = k;
}else {
next[j] = next[k];
}
}
else
{
k = next[k];
}
}
}