今天看了http://blog.csdn.net/chrython/archive/2006/10/05/1321894.aspx 中关于KMP算法的讨论,该文写得很好,感觉有些地方没有说明白 ,因此写下该文中没有说明白的地方.
KMP 算法:
KMP算法主要使用了一个模式串的next数组用来指定当模式不匹配时 模式串应该回到的索引位置 数组大小为模式串的长度(strlen), 它可以将普通的字符串查找效率从O(M*N)减少到O(M+N).
next[] 用来定位当模式不匹配时 模式串应该回到的索引位置.
next[] 的取值K的范围为 -1 <= k < j(j为当前不匹配时模式串的位置索引)
² K = -1 表示原串S 索引前进一步 模式串回到索引位置0
² K = 0 表示原串 S 索引不前进 模式串回到索引位置0
² 0<K<j 表示原串 S 索引不前进 模式串回到索引位置K
next值是根据模式串来计算的.
next值的计算是基于这样一个事实: 当原串与模式串匹配到不相等时, 模式串当前位置的前面元素是 与 原串当前位置前面的元素是相同的.
设原串S = ; 模式串 P = (0 < m <= n);
设当匹配到S, P的位置i ,j 时不相等 也即S[i] != P[j] 并且 P[0]…P[j-1]==S[i-j]… S[i-1] .
1. 当j=0时 令其next值为-1 表示S与P不匹配时, S 因该向前进一步 ,而P此时已在位置0(j=0);
2. 当j>0时,此时有两种情况:
a) 如果P[j] == P[0], 这时隐含了P[0] != S[i], 这时就需要决定S需不需要向前移动了, 显然当P[j]前面 1--K个字 符与开始的1--K个字符相同(1<= K < j)并且P[K] != P[j],此时S不需要移动 而且P将会回到位置K 即 next值为K . 否则 S需要向前移动一步,而P回到位置0 即 next值为-1
b) 这里P[j] != P[0], 意味着P[0] 与S[i] 有可能相同,此时S肯定不会移动, 对于当P[j]前面有1-K个字符与开始 的1--K个字符相同(1<= K < j) 并且 P[K] != P[j] 是 此时P将会回到位置K 即 next值为K 否则为 0
从上面可以得出两个对编写算法很重要的等式: 1、 当P[k] == P[j]时 next[j] = next[k]; 2、当P[k] != P[j]时 next[j] = k. (这里: 1<= K <j) . 原因: 当P[K] == P[j]时 隐藏了P[k] != S[i] 又因为P[k]前面的k个字符与P[j]前面的K个字符是相同的,因此 从P[0]到P[k] 与 从p[k+1] 到P[j]是完全相同的,当S[i]与P[j]比较不相等时,假设这是回到P[k],而P[k] == p[j] != S[i],同样是得到不匹 配的结果, 此时模式串又会回溯, 事实上 当存在P[k] == P[j]时 模式串根本就不会比较到P[j] 当比较到P[k]时 模式串P就会回溯, 这可能 将会导致S向前进一步.
下面是KMP算法的三个发现者写的求next数组的代码 写得很好
void getNext( const char* T, int next[])
{
int j = 0;
int k = -1;// K的最小值
next[0] = -1;
while(T[j] != ‘/0’)
{// 模式串迭代
if(k==-1 || T[K] == T[j])
{// 当T[K ] 与 T[j] 相等时,测试下一个是否相等 如果相等 则 next[j+1] =
// next[k+1] 否则next[j+1] = K+1.
// K==-1时表示了 T[j] == T[0] 且T[0] != T[j-1] T[0]T[1] != T[j-2]T[j-1]...
++k;
++j;
if(T[K] !=T[j])
next[j] = k;
else
next[j] = next[k];
}else{
// K!= -1 且 T[K] 不等于T[J]
k = next[k]; //直接 令k=-1
}
}
}
小结: 实际上有 next[0] = -1 next[1] = 0;
k == -1, T[j] == T[0], 且 T[0]T[1]... != ....T[j-2]T[j-1]
k == 0, T[j] != T[0], 且 T[0]T[1]... != ....T[j-2]T[j-1]
K > 0, T[j] != T[0], 且 T[0]T[1]...T[k] ==T[j-K-1] ....T[j-2]T[j-1] 且T[k] != T[j]
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void getNext(const char *T, int next[])
{
// 求模式串T的next函数值并存入数组 next。
int j = 0, k = -1;
next[0] = -1;
while ( T[j] != '/0' )
{
if (k == -1 || T[j] == T[k])
{
++j; ++k;
if (T[j]!=T[k])
next[j] = k;
else
next[j] = next[k];
}// if
else{
k = -1;
}
}
}
// KMP算法
int KMP(const char* source, const char* pattern,int next[])
{
int i=0;
int j=0;
int plen = strlen(pattern);
int slen = strlen(source);
while(j<plen && i<slen)
{
if(source[i] != pattern[j])
{
if(next[j] == -1)
{
i++;
j=0;
}else if(next[j] == 0)
j=0;
else
j= next[j];
}
else
{
i++;
j++;
}
}
if(j!=plen)
return -1;
else
return i-j;
}
int main()
{
char s[] = "aaaaaaaaaaaaaaaavdaaaaaaaaaaaaaaaaaasaaaaaab";
char p[] = "aaaaaaaaaaaaaab";
int len = strlen(p);
int *next= (int *)malloc(sizeof(int) *len);
getNext(p,next);
//printf("%s /n",strstr(s,p));
printf("%d/n",KMP(s,p,next));
free(next);
return 0;
}