KMP算法
看了一下网上的KMP算法,发现讲的都不大适合我,个人觉得还是严蔚敏的书上讲的比较简单明了清晰,适合我理解,所以就整理了一下。
KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字。其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。
假设主串为's1s2s3...sn',模式串为'p1p2p3...pm',当主串中第i个字符与模式中第j个字符失配时,主串中第i个字符(i指针不回溯)应与模式中那个字符再比较。
假设此时应与模式中第k(k<j)个字符继续比较,则模式中前k-1个字符的子串必须满足下列关系式,且不可能存在k'>k满足下列关系式
'p1p2p3...p(k-1)' = 's(i-k+1)s(i-k+2)...s(i-1)'
而已经得到的"部分匹配"的结果是
'p(j-k+1)p(j-k+2)...p(j-1)' = 's(i-k+1)s(i-k+2)...s(i-1)'
由上述两式可得:
'p1p2p3...p(k-1)' ='p(j-k+1)p(j-k+2)...p(j-1)'
令next[j] = k 则
next[j] = 0 当j = 1 时
= Max{k | 1 < k < j 且 'p1...p(k-1)' = 'p(j-k+1)...p(j-1)'}当此集合不为空时
= 1 其他情况
next函数值 j |1 2 3 4 5 6 7 8
模式串 |a b a a b c a c
next[j] |0 1 1 2 2 3 1 2
int Index_KMP(SString S, SString T, int pos){
//利用模式串T的next函数求T在主串S中第pos个字符之后的位置的KMP算法。其中,T非空,1<=pos<=StrLength(s).
i = pos; j = 1;
while(i <= S[0] && j <= T[0]){
if(j == 0 || S[i] == T[j]){i++;j++} //继续比较后续字符串
else j = next[j]; //模式串向右移动
}
if(j > T[0]) return i - T[0]; //匹配成功
else return 0;
}
上面的next尚有缺陷例如
j | 1 2 3 4 5
模式 | a a a a b
next[j] | 0 1 2 3 4
nextval[j] | 0 0 0 0 4
改进的算法为:
void get_nextval(SString T, int next[]){ //求模式串T的next函数值并存入数组nextval[] i = 1; nextval[1] = 0; j = 0; //i存储的匹配到哪,j为匹配的数值 while(i < T[0]){ if(j == 0 || T[i] == T[j]){ ++i; ++j; if(T[i] != T[j]) nextval[i] = j; else nextval[i] = nextval[j]; }else j = nextval[j]; } }