kmp中最重要的就是字首函式nxt。
定義$nxt[i]$是模式串最長的字首等於其子串$s[1...i]$的真字尾。
顯然$nxt[1]=0$
很容易想到一種暴力模擬的方法(見下面的程式):
for (int i = 2; i <= len(s); i++)
很不幸這樣的複雜度是$o(n^2)$的。
考慮怎麼優化,如果用hash+二分的方法可以優化到$o(n\log)$,已經可以通過大部分的題目。
for (int i = 2; i <= len(s); i++)else
}}
可是這是最優解嗎?答案是否定的,我們還可以優化到$o(n)$。
我們發現剛才的方法都沒有好好利用已經求出的$nxt$,我們想想怎麼利用這些已知資訊。
假設我們現在要求$nxt[i]$,記$j=nxt[i-1]$。則顯然$s[1...j]=s[i-j...i-1]$,如果$s[j+1]=s[i]$的話,則$nxt[i]=j+1$。
如果不是呢?能不能如果直接令$j=j-1$,則這和暴力差不多。
上面的思路和之前犯了同樣的錯誤——沒有考慮已知資訊。
如果每次令$j=nxt[j]$,則會的得到正確的答案(證明易證,畫個圖就好了)。
可以通過勢能分析的方法證明其複雜度是正確的。
for (int i = 2, j = 0; i <= len(s); i++)
kmp演算法和求nxt大同小異,如果我們每次記錄乙個f陣列$f_i$代表文字串以第i個字元結尾的字尾與模式串的字首的最長公共長度。
求法與求nxt基本一樣,只用加一句如果當前長度為模式串大小也跳nxt。
//s是模式串,t是文字串
for (int i = 1, j = 0; i <= len(t); i++)
當然,字首函式不止做字串匹配這一種作用。
週期,即可以重複多次而得到原串的串。
如abaaba的最小週期就是aba。
aabbaabbba
具體怎麼求呢?
如果$n-nxt[n]|n$,則為$n-nxt[n]$,否則為n。
首先可以證明這樣做是正確的。
如果兩個nxt有相交,且滿足上述要求,易證這是對的。
如果沒有相交,則不可能滿足上述要求。
再證這是最小的,可以用反證法,如果還有更小的,則可以推出還有更大的nxt[n],矛盾。
首先什麼是dfa(確定有限狀態自動機),其實就是對於每個狀態,和字符集,都可以轉移到另乙個狀態。
狀態在kmp這就是匹配長度。
我們現在需要求出乙個陣列$ch[i][j]$表示狀態i後加乙個字元j會得到哪個狀態。
有初值$ch[0][s[1]]=1$。
有乙個直觀的想法就是列舉每一位和下一位的字元,然後暴力跳nxt。
這樣固然可做,但還是犯了沒有利用已知資訊的錯誤。
如果$j=s[i+1]$,那直接指向i+1。
如果不,可以指向$ch[nxt[i]][j]$,這一步很好理解,因為$nxt[i]例題:[hnoi2008]gt考試
我的字串下標都是從1開始的
現在要你求乙個陣列z,$z_i$代表字串s的字尾$s[i...|s|]$的最長和s相同的字首。
求法類似manacher。
首先$z_1=|s|$不需要我們求。
從$i=2$開始,同時維護兩個指標$r,l$,分別指向歷史最遠匹配位置,和那次匹配的初始位置(是不是類似馬拉車)。
如果$i \leq r$,根據已知資訊可以得到$z[i] \geq z[i-l+1]$,可以先令$z[i]=z[i-l+1]$,然後再暴力匹配。
和馬拉車一樣,不能超過已知範圍,所以還要和$r-i+1$取個min。
之後就可以暴力匹配了。
因為r是在不斷往後移動的,所以複雜度是$o(|s|)$的(是不是和馬拉車一模一樣)。
void z(char *s, int *z)}}
現在給你兩個字串s,t。讓你求s的每乙個字尾與t的最長公共字首的長度。
對於字尾i,我們把答案存到$p_i$中。
這其實就是exkmp。
就好像kmp和求nxt很像,exkmp和z函式也很像。
只用把所有和文字串有關的z改成p(即取min中的z不動)。
然後暴力匹配時把i+z[i]改成文字串即可。
void exkmp(char *s, char *t, int *p, int *z)}}
現在你可以acluogu的模板題了(
exkmp還有一種不優秀(我覺得)的寫法,我會在附錄a中給出。
例題:[gdoi2014]beyond
如果第乙個字串的字首$s1[1...i]$是環的話則必然有乙個$1 \leq j < i$,滿足$s1[1...j]=s2[j+1...i],s1[j+1...i]=s2[1...j]$。
所以要做兩遍exkmp,定義p1為s1位文字串,s2為模式串的p,p2反過來定義。
所以我們現在找到乙個位置i,則$s2[1...p1[i]]=s1[i...i+p1[i]-1]$,現在只要$p2[p1[i]+1] \geq i-1$,即可以夠到i即可滿足條件。
將答案與$i+p1[i]-1$取max,最後輸出答案。
即$ans = max_ i+p1[i]-1$
#include usingnamespace
std;
const
int n = 2000010
;int
n;char
s1[n], s2[n];
intz1[n], z2[n];
intp1[n], p2[n];
intans;
void z(char *s, int *z)
}}void exkmp(char *s, char *t, int *p, int *z)
}}void cmax(int &x, int
y) int
main()
完結撒花✿✿ヽ(°▽°)ノ✿。
void get_nxt(char t, intp) }
}void exkmp(char s, char t, int
p) }
}
這裡nxt是z,extend是p。
如果遇到其他有關kmp和exkmp的套路或有趣的思維我會補上
KMP 擴充套件KMP
本文將不斷加入例題,稍安勿躁,今天的總結爭取9 30寫完.kmp,中文名字叫字串匹配,用於解決一類字串匹配問題.先下一些定義 首先我們先想一想 nxt i 對於求解問題有怎樣的幫助.我們對於每乙個 t i s 1 的位置都匹配一次,這樣子複雜度為 theta n m 的.考慮在暴力匹配中其實我們不一...
擴充套件kmp
出自 2 i k l 1 p k,即i l p。這時,首先可以知道a i.p 和b 0.p i 是相等的 因為a i.p b i k.p k 而i k l 1 p k,由b 0.l 1 b i k.i k l 1 可得b 0.p i b i k.p k 即a i.p b 0.p i 然後,對於a p...
擴充套件KMP
拖了這麼久,終於打出擴充套件kmp了。並不長,但是細節很多。最好把模板背下來,實在背不下來就根據原理去推。相比於kmp來說擴充套件kmp的應用範圍更廣,更靈活。它的ext i 與kmp的next i 的區別就是next i 表示長度最大的一段s i next i 1 i t 1 next i ext...