目錄完整**
最初學這個演算法,是在新初一的暑假裡,乙個初三即將畢業的學長給我們講的。當時聽得不是很懂,模板題也就只有 28pts。
後來過了好久,又想起來重新學一遍字串,從 kmp 到 sam 都來一遍。於是便有了這片文章。
kmp 演算法是由三名姓氏首字母分別為 k、m、p 的牛人發明的演算法。雖然我不知道他們到底是誰hh
就像標題中那樣說的,kmp 是字串的匹配演算法。
那什麼叫做匹配呢?看下圖就知道了:
接下來,我們會大量使用此類稱呼,請務必記住這幾個名稱!
注 1:若未特別說明,下面用 \(n\) 表示文字串 \(s\) 的長度,\(m\) 表示模式串 \(t\) 的長度。
眾所周知,很多優秀的演算法都由於暴力。kmp 也不例外。我們先從乙個一般的暴力演算法開始:
初始化,\(i=1\),\(j=1\);
開始匹配,如果 \(s_i=t_j\),則 \(i\leftarrow i+1\),\(j\leftarrow j+1\);
如果 \(s_i\neq t_j\)(也稱為失配),則重新令 \(i\leftarrow i-j+2\),\(j\leftarrow 1\);
特別地,如果 \(j=m\),即得到一次匹配,則令 \(i\leftarrow i-m+2\),\(j\leftarrow 1\)。
極其暴力,如果失配就從第乙個沒有匹配過的點開始。如果字串是隨機的可以跑得很優秀(因為很快就會失配),但是如果是精心構造過的資料就會被輕易卡到 \(\mathcal(nm)\) 從而**。
怎麼給這種演算法加上優化呢?
我們注意到,之前的失配是非常浪費時間的。如果我們能夠盡量利用失配前得到的結果,是不是就能在一定程度上提高程式效率,同時保證正確性呢?
每一次匹配都為了找出可能存在的匹配方案,那麼如果某些點可以在一開始就被證明是不可能存在匹配的,是不是就能節省很多時間呢?
如下圖:
在 kmp 演算法中,這樣的字首/字尾被稱為 border(有些也稱為 next 陣列)。這是整個 kmp 演算法優化的關鍵。
這樣,我們就能完成匹配後的優化。那麼怎麼解決失配的優化問題呢?其實很簡單。
失配是什麼?其實是模式串的乙個字首的匹配。所以我們也可以用匹配的優化來優化失配,只是指標的邊界處理略有不同而已。
經過了這樣的優化,匹配的時間複雜度可以降到多少呢?注意到指向文字串的指標只增不減,而指向模式串的指標也是只增不減。每一次比較必然帶來兩個指標至少乙個的變化,所以複雜度是 \(\mathcal(n+m)\),有了極大的優化。
這一段的 code:(我的寫法的下標從 \(0\) 開始)
ptr = -1;
for (int i = 0; a[i]; i++) // 指向文字串的指標
if (a[i] == b[ptr+1]) // 這麼寫而不是 (ptr == -1) 是因為最後一位也有可能匹配成功 }
}
講了這麼多,我們似乎沒有辦法很快地求出 border,而且是模式串所有字首的 border。我們需要引入 dp 思想。
設 \(f_i\) 為到第 \(i\) 位的字首的 border 長度。怎麼轉移呢?
首先根據 border 的定義,如果 \(t_+1}=t_i\),也就是可以從上一位轉移,即 \(f_i=f_+1\)。但是如果失配了呢?
這也是 kmp 極其精髓的一點,我們要證明乙個定理,從而極大地優化複雜度:
theorem:乙個 border 的 border 是原串的次大border,即使字串字首和長度相等字尾一樣的長度次大的方案。
一張圖看懂證明(字串用線段表示):
這個性質至關重要,也就是說我們可以通過遞迴的方式來依次尋找最大、次大,從而轉移。
有了這個性質,之前那個用遞迴方式進行指標移動的思路也明白了。那麼……複雜度呢?這個玄學的複雜度怎麼分析呢?(我不會)總之,這裡的複雜度也是線性的。
貼**時間到:
fail[0] = -1, ptr = -1;
for (lb = 1; b[lb]; lb++) // 遍歷模式串
if (b[ptr+1] == b[lb]) // 理由同上
ptr++;
fail[lb] = ptr; // 陣列實際上存的是指標
}
kmp 演算法的**,可以通過模板題
#include using namespace std;
const int max_n = 1000000;
char a[max_n], b[max_n];
int fail[max_n+1];
int main()
if (b[ptr+1] == b[lb])
ptr++;
fail[lb] = ptr;
} ptr = -1;
for (int i = 0; a[i]; i++)
if (a[i] == b[ptr+1])
}} for (int i = 0; i < lb; i++)
printf("%d ", fail[i] + 1);
putchar('\n');
return 0;
}
KMP字串匹配。。學習。
這個演算法時間複雜度o m n 不回溯例如 a c b c a c b 1 0 0 1 0 1 2 這個是next陣列 當你每次匹配失敗時候。這個匹配串的作用 例如 當時是下標是j,匹配失敗了,就要回到next j 1 1 你失敗那個位置之前一步肯定沒失敗吧 不然就不匹配到這了,然後就回到上一步那個...
字串匹配KMP演算法學習筆記
字串匹配,leetcode28題。時間複雜度o mn 的演算法大家都會,題解裡面官方賬號給出的利用字串雜湊判等的o n 演算法也很優秀。本篇的重點是kmp演算法。如果還不是很好理解,可以看labuladong的題解。乙個kmp演算法易理解版本,講解的部分也很清楚。本篇主要是對此題解做乙個補充,個人感...
kmp字串匹配
首先要對模式串進行預處理。預處理過程就是計算出指定位置的字首和字尾的最大相同的長度 啊啊啊啊。估計只有我乙個人能看懂 這個文章說得很清楚 比如說 a a a c b c a a a 0 1 2 0 0 0 1 2 3 void getnext int next,char par 20 int n 翻...