朴素的字符串匹配算法:
文本串text[]和模式pattern[]匹配,自然的想法是O(n^2)算法,拿pattern[]的字符从头到尾和text[]匹配,设i为text[]当前下标,j为pattern[]当前下标,每一次text[i] != pattern[j],下标j将重新归0,i加一,然后重复。
int Brute_Force(){
int i=0, j=0;
int m = strlen(text);
int n = strlen(pattern);
while(i<m && j<n){
if(text[i] == pattern[j]){
++i; ++j;
}
else{
i -= (j-1); j = 0;
}
}
//...
}
KMP算法:
观察上面的BF算法,不难发现每次匹配失败,j都要重置为0。KMP算法改进了BF算法,使得j不需要每次都重置为0.
kmp算法的思想:
倘若text[]和pattern[]能局部匹配,那么当某次匹配失败时(设text[]此时下标为i, pattern[]下标为j),pattern[j] != text[i], 那么我们可以知道,
text[i]左边的若干个连续字符和pattern[]匹配,同时我们可以想象,pattern[]相对于text[]右移若干个单位长度,即可以使得pattern[]和text[]重新匹配。我们有需要如下的几个条件:
(1)pattern[0, j) == text[ i-j, i) 即失配位置的左边为匹配的。
(2)pattern[0, t) == text[i-j, i) 即pattern相对于text右移了j-t个单位后,使得pattern[]和text[]重新匹配。
(3)pattern[j-t, j) == text[i-j, i) 这点联系第一点不难得知。
(4)于是,
对比(2),(3)我们得到:pattern[0, t) == pattern[j-t, j) 。
即在pattern[0, j)中长度为t的真前缀,要和长度为t的后缀完全匹配。
(5)观察加想象,为了保证pattern[]右移(j-t)个长度不遗漏任何情况,我们应该使得j-t尽可能的小,即t尽可能的大。
(6)
失配后的下一次匹配位置是pattern[t]
到目前为止,实现kmp算法还需要求解t的值。再观察以上得到的结论,我们发现,
t值和text[]无关! 求t值现在转换成了求pattern[0, j)最长真前缀和最长真后缀,且真前缀和真后缀完全匹配(相同)。进一步,我们顺其自然的知道,可以先预处理出t的值,之后重复使用即可。我们将t的值写在一个next[]数组里。