KMP
//www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
//www.acwing.com/solution/content/23907/
先上兩個大佬的博客
能懂基本的思想了
第二個圖來自acw的第二個題解
看到這裡應該就能明白了
OK了,了解到這裡就能去聽y總的課了…
next[i]表示以i為終點的後綴與和從1開始的前綴相等且這個後綴的長度最長,其實也就是之前博客里又說的匹配表
把子串單獨拎出來看
然後移動之後就又開始看看下一個點是否匹配了
好了,現在過程都懂了,開始寫代碼:
主要分為兩個部分:
第一個部分:字符串的匹配
這個是acw下面的一個評論,本來剛開始還不是很理解,現在很理解了
//KMP的匹配過程 for(int i=1;j=0;i<=m;i++) { while(j&&s[i]!=p[j+1]) j=ne[j];//這是一個往後退的過程,找到一個類似於前綴的另一個與s串匹配的位置 if(s[i]==p[j+1]) j++;//如果退無可退之後還不滿足條件,那就到i的下一個位置 if(j==n) { //完成匹配 } }
NND,終於懂了…..
刷了三天,知道是個啥意思了,hhh
#include<iostream> using namespace std; const int N=100010,M=1000010; char q[N],s[M]; int ne[N];//保存next數組 int main() { int n,m; cin>>n>>q+1>>m>>s+1;//下標均從1開始 for(int i=2,j=0;i<=n;i++) //j表示匹配成功的長度,i表示q數組中的下標,因為q數組的下標是從1開始的,只有1個時,一定為0,所以i從2開始 { while(j&&q[i]!=q[j+1]) j=ne[j]; //如果不行可以換到next數組 if(q[i]==q[j+1]) j++; //成功了就加1 ne[i]=j; //對應其下標 } //j表示匹配成功的長度,因為剛開始還未開始匹配,所以長度為0 for(int i=1,j=0;i<=m;i++) { while(j&&s[i]!=q[j+1]) j=ne[j]; //如果匹配不成功,則換到j對應的next數組中的值 if(s[i]==q[j+1]) j++;//到退無可退的時候,就進行下一個i //匹配成功了,那麼j就加1,繼續後面的匹配 if(j==n)//如果長度等於n了,說明已經完全匹配上去了 { printf("%d ",i-j); //因為題目中的下標從0開始,所以i-j不用+1; j=ne[j]; //為了觀察其後續是否還能跟S數組後面的數配對成功 } } return 0; }
代碼還有點小細節懂的不是很徹底,再刷刷吧…
********************************哈哈哈哈,新的一刷又來啦
之前開頭的那個;鏈接僅僅了解一下專有名詞就好,具體代碼的實現和那個還是有一點點差別的
//www.bilibili.com/video/BV1Qb411h7U6/
配上這個動畫,再加上我下面的講解簡直一目了然,我吹的,看個人理解
其實整個KMP的過程,就是一直頻繁移動子串也就是p串的一個過程,可是這個是代碼啊,怎麼可能真的實現移動
但是可以實現指針的移動,也就是剛剛上面所說的退的情況,恕我直言,那個說法對我來說挺不好理解的
現在換一個思路,比如ABCEABDE為s串,ABD為模板串,也就是p串,在C不匹配的時候,直接移動ABD到ABDE的那個位置與之對齊(但實際上就是指針重新恢復到剛開始的那個位置啦)
然後就是移動i的事情啦,因為s[i]==p[j+1]j才會++,如果不滿足條件那就是i++,直到i指針i指針與移動之後的ABD第一個開始匹配(並不是移動的哦,只是指針又指到匹配的那個位置了)
這只是我自己的理解,覺得利於我的理解,一定要配上動畫啊,不然有些乾癟,hhh
其實剛剛所說的就是這個圖