【字串】【KMP】
阿新 • • 發佈:2022-03-18
【字串】【KMP】
0. 引入
先看一道模板題:
【模板】KMP字串匹配
要求一個字串(稱為模式串)在另一個字串(稱為文字串)中的所有出現位置,樸素的方法是對於文字串的每一位,檢查以該位為起點,是否能匹配到一個模式串。複雜度O(NM)
當然我們可以用字串雜湊優化到線性,但有更準確的做法:KMP
1. KMP
KMP的基本功能就是進行單個字串匹配(多個要用到AC自動機)
KMP有兩種理解方式:一種是靜態的Lborder,一種是動態的(失配後指標移向哪一位),這裡主要講前者。
2. 定義:
一個字串的border定義為既是其字首,也是其後綴,但不為其本身的串。
一個字串的Lborder定義為其最長的border
3. 性質:
- border的border還是border
利用這個性質,要找到一個串的所有border,只要不停的跳這個串的Lborder即可。
因此,一個串所有字首的Lborder構成一棵樹,其中根節點為0。很多題目可以利用這個樹形結構來解題。
2.每個border都與一個不嚴格迴圈節一一對應。
也就是說,如果m是長為n的字串S的一個border,那麼n-m就是S的一個不嚴格迴圈節,反之亦然。
因此找到一個穿的所有不嚴格迴圈節,只要列舉一個串的所有border即可。
而嚴格的迴圈節就是使得(n-m)|n的長為m的border。
4. 字串匹配
- nxt陣列
nxt[i]表示字串長度為i的字首的Lborder的長度
如何快速求出nxt陣列?
採用遞推,假設已知nxt[1~(i-1)],若求nxt[i],可令j=nxt[i-1]。
檢查若s[j+1]==s[i],則令nxt[i]=j+1,否則令j=nxt[j],繼續檢查。重複這一步直到匹配成功或j=0
可以證明覆雜度O(|S|)
CODE:
void getnxt()
{
for(int i=2;i<=m;++i)
{
int j=nxt[i-1];
while(j&&b[j+1]!=b[i]) j=nxt[j];
if(b[j+1]==b[i]) nxt[i]=j+1;
}
}
- 匹配
與求nxt的過程類似,記i表示當前文字串匹配到第i位,j表示模式串最多前j位能與文字串前i位構成的子串中後j位進行匹配。
當i++時,則檢查若s[j+1]==s[i],則令j++,否則令j=nxt[j],繼續檢查。重複這一步直到匹配成功或j=0
若j=m,則記錄答案,並令j=nxt[j]。
可以複雜度O(|N|+|M|)
CODE:
void kmp()
{
for(int i=1,j=0;i<=n;++i)
{
while(j&&b[j+1]!=a[i]) j=nxt[j];
if(b[j+1]==a[i]) j++;
if(j==m)
{
write(i-m+1);puts("");
j=nxt[j];
}
}
}