1. 程式人生 > 其它 >【字串】【KMP】

【字串】【KMP】

【字串】【KMP】

0. 引入

先看一道模板題:
【模板】KMP字串匹配
要求一個字串(稱為模式串)在另一個字串(稱為文字串)中的所有出現位置,樸素的方法是對於文字串的每一位,檢查以該位為起點,是否能匹配到一個模式串。複雜度O(NM)
當然我們可以用字串雜湊優化到線性,但有更準確的做法:KMP

1. KMP

KMP的基本功能就是進行單個字串匹配(多個要用到AC自動機)
KMP有兩種理解方式:一種是靜態的Lborder,一種是動態的(失配後指標移向哪一位),這裡主要講前者。

2. 定義:

一個字串的border定義為既是其字首,也是其後綴,但不為其本身的串。
一個字串的Lborder定義為其最長的border

3. 性質:

  1. border的border還是border
    利用這個性質,要找到一個串的所有border,只要不停的跳這個串的Lborder即可。
    因此,一個串所有字首的Lborder構成一棵樹,其中根節點為0。很多題目可以利用這個樹形結構來解題。
    2.每個border都與一個不嚴格迴圈節一一對應。
    也就是說,如果m是長為n的字串S的一個border,那麼n-m就是S的一個不嚴格迴圈節,反之亦然。
    因此找到一個穿的所有不嚴格迴圈節,只要列舉一個串的所有border即可。
    而嚴格的迴圈節就是使得(n-m)|n的長為m的border。

4. 字串匹配

  1. 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;
	}
}
  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];
		}
	}
}