擴充套件KMP模板(學習)

2022-05-06 15:33:10 字數 4257 閱讀 5877

乙個算是冷門的演算法(在競賽上),不過其演算法思想值得深究。

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];

//i相當於k+1

//nxt[i-p0]相當於l

//extend[p0]+p0相當於p

//因為在**裡我是從0開始記字串的,所以本應在小於號左側減1,現在不用了。

也就是s[k+l]s[k+l]這個位置在p前面,如圖:

圖可能略醜

同樣,我們設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;

now=max(now,0);//

這裡是防止i>p

while(t[now]==s[i+now]&&now

暴力求解的過程

extend[i]=now;

p0=i;//

更新p0

求extendextend的大部分過程已經完成了,現在就剩怎麼求nextnext了,我們先摸清一下求nextnext的本質:

求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#include

using

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