终于认真的学习了一下KMP
我觉得这篇文章最好懂http://kb.cnblogs.com/page/176818/
首先,我们应该知道KMP是干什么的。
举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"?
1.
首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。
2.
因为B与A不匹配,搜索词再往后移。
3.
就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。
4.
接着比较字符串和搜索词的下一个字符,还是相同。
5.
直到字符串有一个字符,与搜索词对应的字符不相同为止。
6.
这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。
7.
一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。
但是,我们要后移几个位置呢?
显然,对于上方的例子,我们需要这么对齐
BBC ABCDAB ABCDABCDABDE
ABCDABD
因为我们其实知道前面六个字符是"ABCDAB",下一个可能能匹配成功的位置即如上面所示(且此时上面下面两个串的AB都不需要在匹配,直接开始匹配“C”与“ ”)
那我们想想看,这个位置需要满足什么条件呢?
即对于更一般的,下图中若匹配到蓝色位置,红串与黑串不匹配了,我们把黑串移到绿串的位置应该满足什么条件呢?
由于蓝色部分之前不需要再匹配,所以两条紫色段必相等
由于绿串和黑串是同一个串(只是把黑串向后移),所以下列两条紫色段相等
所以这两条紫色段也相等
由于红串和黑串在蓝线前的部分都相等,所以下图两段紫色部分也相等
所以这两段紫色部分相等
这时,我们发现了什么?黑串在蓝色部分前的前缀和后缀相等(紫色部分)!
同理,我们也可以由最后一幅图的紫色部分相等推出第一幅图的紫色部分也相等。
且为了避免漏掉答案,紫色部分应是最长的!(否则如下图的黄色部分若相等,把黑串移到橙串位置后绿串的情况会遗漏)
移到绿色部分后,由于棕色部分一定相等,则还是有可能移到橙串位置(如果蓝色部分还是不匹配(否则橙串一定无解,也应跳过))
总结起来,一个串匹配失败时,向后移到的位置应是匹配失败位置前的子串的最长公共前缀与后缀的后缀的开头位置!!!
但是,之后程序实现时我们还有更简单的移动方式(原理和上面一模一样,到时候再说,我先讲讲最长公共前缀与后缀的长度该怎么求)
我们用nxt[i]表示0~i的字符串最长公共前缀与后缀的长度
void init(char *s) { nxt[0]=-1;int len=strlen(s); int i=0,j=-1; while(i<len) { if(j==-1||s[i]==s[j]) { i++;j++;nxt[i]=j; } else { j=nxt[j]; } } } int kmp(char *s,char *t,int l) { int lens=strlen(s),lent=strlen(t);int r=lent-1; int j=0,i=0; while(i<lens) { if(j==-1||s[i]==t[j]) { i++;j++; } else { j=nxt[j]; } if(j==lent)return i; } return -1; }