是不是每个程序员都要在人生的某个时刻把KMP算法彻底搞懂?之前写过几次KMP了,每次都是现学现写,感觉没有真正掌握,这次写一篇文章整理总结一下,希望下次能直接写出来。这是我学习过程中看的几篇文章,感谢博主们。但是之前有次还看过一篇更透彻的……忘了是不是在优快云了,找不到了。
https://blog.youkuaiyun.com/Heathy__/article/details/60875067
https://blog.youkuaiyun.com/v_july_v/article/details/7041827
主要过程:
(P[a,b]表示包含P[a]不包含P[b]的子串)
1、现在我们想知道P串是否是S串的子串。我们从头开始,依次检查P串的每一位,和S串的每一位,现在我们检查到P[j] 和S[i],即P[0:j]==S[i-j:i],当前有两种可能:
①P[j]==S[i],继续检查下一位;
②P[j]!=S[i]:这说明从S[i-j]这一位开始的子串!=P, 一般来说,我们下一步是检查S[i-j+1]开始的子串能不能==P,也就是P要从头检查了,而S也好像前功尽弃,要退回到S[i-j+1]位置重新检查。
然而 ∵P[0:j]==S[i-j:i] ∴P[j-k:j]==S[i-k:i]
如果 P[0:k]==P[j-k:j],那么 P[0:k]==S[i-k:i], 也就是说如果我们能提前知道一些P串的信息,即求得k,就相当于P[0:k]的检查已经完成,下一步检查P[k]和S[i]的对应情况。这样,对S串的扫描是一次的,对P串,我们需要知道P[j]位不对应时的k。
2、下面我们对P串预处理,用一个next数组存储k,即next[j]=k。这一步不需要S串。
∵P[0:next[j]]==P[j-next[j]:j]
①如果P[j]==P[next[j]],那么P[0:next[j]+1]==P[j+1-next[j-1]-1:j+1], 即 next[j+1]==next[j]+1
②如果P[j]!=P[next[j]],请注意红色的式子包含了很多信息,比如令s=next[j](s有意义) P[0:s]==P[j-s:j] 那么P[0:next[s]]==P[j-next[s]:j] (把上面的绿式子,S换成P,i换成j,就能得到) 现在我们得到了一个和红式子长得很像的蓝式子,我们又要开始讨论:
①如果P[j]==P[next[s]],那么……,next[j+1]==next[s]+1
②如果P[j]!=P[next[s]], ……
……
够幸运的话,我们很快就能算出next[j+1];或者,我们会发现没法再用令a=next[b]这个代换了(我们上面用了s=next[j]),也就是a是负数或者0的情况(P[0:0]是个空串),这说明绿式子第二行的“如果”假设不成立,我们没法找到一个k来简化过程。以k==0为例:就相当于P[0:0]的检查已经完成,下一步检查P[0]和S[i]的对应情况。所以next[j+1]=0
以上说明可能不太形象,可配合画图食用。但是我个人感觉画图虽然理解容易,但是不如数学语言更容易转化成代码。
(实际上写的是next[j] = k-1,为了好写
C++:
#include <iostream>
using namespace std;
int nxt[1000];
//char S[1000] = "ababcababcababababcabaabc";
char S[1000] = "ababcababacbabababcabcab";
char P[1000] = "ababcabaa";
int main()
{
int k=0, j = 1;
nxt[0] = -1;
while(P[j]){
if(P[j]==P[nxt[k]+1]) {
nxt[j] = nxt[k]+1;
k = j++;
}
else {
k = nxt[k];
if(k<0){
nxt[j] = -1;
k = j++;
}
}
}
/*j = 0;
while(P[j]){
cout<<nxt[j++]<<' ';
}*/
int i = 0;
bool flag = 0;
j = 0;
while(S[i]){
if(S[i]==P[j]){
i ++;
j ++;
if(!P[j]) {flag = 1; break;}
}
else{
if(j==0) i ++;
else j = nxt[j]+1;
}
}
if(flag) cout<<"Y";
else cout<<"N";
return 0;
}
Python:
nxt = {0:-1}
#S = "ababcababcababababcabaabc"
S = "ababcababacbabababcabcab"
P = "ababcabaa"
k = 0
j = 1
while j<len(P):
if P[j]==P[nxt[k]+1]:
nxt[j] = nxt[k]+1
k = j
j += 1
else:
k = nxt[k]
if k<0:
nxt[j] = -1
k = j
j += 1
i = j = 0
flag = 0
while i<len(S):
if S[i]==P[j]:
i += 1
j += 1
if j>=len(P):
flag = 1
break
else:
if j==0: i += 1
else: j = nxt[j]+1
if flag: print "Y"
else: print "N"
(Python差不多就是翻译的C++,Python太久不看了都忘了,正在慢慢温习。)