乙個算是冷門的演算法(在競賽上),不過其演算法思想值得深究。
kmp的演算法思想,具體可以參考這篇**。
trie樹(字典樹)。
擴充套件kmp的模板問題:
給你兩個字串s,t,長度分別為n,m。雜湊是不可能的,這輩子都不可能的。請輸出s的每乙個字尾與t的最長公共字首。
\mathcalac自動機?好像更不可做了。
我們先定義乙個:extend[i]extend[i]表示s[i...n]s[i...n]與tt的最長公共字首長度,而題意就是讓你求所有的extend[i]extend[i]。
注:以下字串均從1開始計位。如果s=aaaaaaaaaabaas=aaaaaaaaaabaa,n=13n=13
由圖可知,extend[1]=10extend[1]=10、extend[2]=9extend[2]=9。
我們會發現:在求extend[2]extend[2]時,我們耗費了很多時間,但我們可以利用extend[1]extend[1]來更快速地求解:
因為已經計算出extend[1]=10extend[1]=10。
所以有:s[1...10]=t[1...10]s[1...10]=t[1...10]
然後得:s[2...10]=t[2...10]s[2...10]=t[2...10]
因為計算extend[2]extend[2]時,實際上是s[2...n]s[2...n]和tt的匹配,
又因為剛剛求出了s[2...10]=t[2...10]s[2...10]=t[2...10],
所以匹配的開頭階段是求t[2...10]t[2...10]與tt的匹配。
這時我們可以設定輔助引數:nextnext,next[i]next[i]表示t[i,m]t[i,m]與tt的最長公共字首長度。
那麼對於上述的例子:next[2]=10next[2]=10
即:t[2...11]=t[1...10]t[2...11]=t[1...10]
然後得:t[2...10]=t[1...9]t[2...10]=t[1...9]
∴s[2...10]=t[2...10]=t[1...9]∴s[2...10]=t[2...10]=t[1...9]
也就是說求extend[2]extend[2]的匹配的前9位已經匹配成功了,不用再匹配一遍了,可以直接從s[11]s[11]和t[10]t[10]開始匹配,這樣我們就省下了很多時間。
這其實就是kmp的思想。
設extend[1...k]extend[1...k]已經算好,並且在以前的匹配過程中在s串中的最遠位置是pp,即p=max(i+extend[i]-1)p=max(i+extend[i]−1),其中i=1...ki=1...k。
然後我們設取這個最大值k的位置是p0p0。
首先,根據定義,s[p0...p]=t[1...p-p0+1]s[p0...p]=t[1...p−p0+1]。
我們設t[k-p0+1]t[k−p0+1]在tt串中對應的位置為aa,t[k-p0+2]t[k−p0+2]在tt串中所對應的位置為bb。(僅僅是為了下面的講解方便)
然後令l=next[b]l=next[b]。
下面分兩種情況討論:
也就是s[k+l]s[k+l]這個位置在pp前面,如圖:
我們設l1=1l1=1,r1=lr1=l,l2=bl2=b,r2=b+l-1r2=b+l−1。(bb的定義在上一張圖)
此時l1l1、r1r1、l2l2、r2r2的位置如圖所示。
也就是說,t[l1...r1]=t[l2...r2]t[l1...r1]=t[l2...r2]。
即\color}紅線與\color}綠線與\color}藍線相等。
然後由nextnext的定義可知,t[r1+1]!=t[r2+1]t[r1+1]!=t[r2+1]。
又因為t[r2+1]=s[k+l+1]t[r2+1]=s[k+l+1]
所以t[r1+1]!=s[k+l+1]t[r1+1]!=s[k+l+1],這兩個字元不一樣。
又因為\color}紅線與\color}藍線相等,這兩條線已經匹配成功。
所以extend[k+1]=lextend[k+1]=l,也就是next[b]next[b]。
所以這段的**比較簡單:
if(i+nxt[i-p0]p0];也就是s[k+l]s[k+l]這個位置在p前面,如圖://i相當於k+1
//nxt[i-p0]相當於l
//extend[p0]+p0相當於p
//因為在**裡我是從0開始記字串的,所以本應在小於號左側減1,現在不用了。
圖可能略醜
同樣,我們設l1=1l1=1,r1=lr1=l,l2=bl2=b,r2=b+l-1r2=b+l−1。
此時l1l1、r1r1、l2l2、r2r2的位置如圖所示。(r1r1的位置可能在p-p0+1p−p0+1前或後)
同理,\color}紅線與\color}綠線與\color}藍線相等。
那麼我們設(k+l)(k+l)到pp的這段距離為xx。
那麼s[k+1...(k+l)-x+1]=s[k+1...p]s[k+1...(k+l)−x+1]=s[k+1...p]。
又因為:
t[l1...r1-x+1]=t[l2...r2-x+1]=s[k+1...(k+l)-x+1]t[l1...r1−x+1]=t[l2...r2−x+1]=s[k+1...(k+l)−x+1]
即\color}\color\color}\color\color}s1=s2=s3。
所以t[l1...r1-x+1]=s[k+1...p]t[l1...r1−x+1]=s[k+1...p],
也就是說t[1...r1-x+1]=s[k+1...p]t[1...r1−x+1]=s[k+1...p],這一段已經匹配成功了。
即\color}s1與\color}s2是相等的,已經匹配成功了。
那麼我們就可以從s[p+1]s[p+1]和t[r1-x+2]t[r1−x+2]開始暴力匹配了,無需再考慮前面的東西。
那麼這段的**長這樣:
int now=extend[p0]+p0-i;求extendextend的大部分過程已經完成了,現在就剩怎麼求nextnext了,我們先摸清一下求nextnext的本質:now=max(now,0);//
這裡是防止i>p
while(t[now]==s[i+now]&&now
暴力求解的過程
extend[i]=now;
p0=i;//
更新p0
求t的每乙個字尾與t的最長公共字首長度聽起來好熟悉,我們再看一下題面:
求s的每乙個字尾與t的最長公共字首長度我們發現求nextnext的本質和求extendextend的本質是一樣的,所以我們直接複製重新打一遍就好了。
這其實和kmpkmp的思想很相似,因為kmpkmp也是自己匹配一遍自己,再匹配文字串。
要注意的一點是:求nextnext時我們要從第2位(也就是**中的第1位)開始暴力,這樣能防止求nextnext時引用自己nextnext值的情況。
因為求nextnext的時間複雜度是o(m)o(m),求extendextend的時間複雜度是o(n)o(n),所以總時間複雜度:o(n+m)o(n+m),即ss串與tt串的長度之和。
#include#includeusing
namespace
std;
const
int maxn=1e6+5
;string
s,t;
intq,nxt[maxn],extend[maxn];
void
getnxt()
}}void
exkmp()
}}int
main()
擴充套件kmp模板
擴充套件kmp詳解 擴充套件 kmp 演算法 輔助陣列next i 表示t i,m 1 和t的最長公共字首長度 設定兩個變數,a 和 p。p 代表以 a 為起始位置的字元匹配成功的最右邊界,也就是 p 最後乙個匹配成功位置 1 模板 p5410 模板 擴充套件 kmp 例題 hdu 4333 rev...
擴充套件KMP模板
具體原理可以參考這裡 擴充套件kmp 擴充套件kmp演算法 劉毅 1 include2 include3 include4 include5 using namespace std 6const int n 1e6 5 7 8int len1,len2 9int nxt n extend n nxt...
ACM常用模板 擴充套件KMP
模板 擴充套件kmp,用extend i 儲存 主串 s i.n 1 與 模式串 t的最長公共字首的長度 using namespace std intne maxn extend maxn void ekmp chars,chart s為主串,t為模版串 j 0 while j len1 j le...