kmp即其擴充套件

2022-05-11 15:49:33 字數 3219 閱讀 2536

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 using

namespace

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, int

p) }

}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...