模式串匹配 KMP演算法和AC自動機(二)

2021-08-31 01:35:18 字數 2771 閱讀 2380

部落格《模式串匹配:kmp演算法和ac自動機(二)》中講了kmp原理和實現,kmp演算法針對的是單模式串的匹配問題,而ac自動機是能夠解決多模式穿匹配問題的演算法,曾嘗試自己實現乙個ac自動機,但是發現還是挺有難度的,於是在網上看了一些大神的模板,在這裡**學習一下~

下面的部落格**:

kmp 大多 是用來解決 單串單串匹配 的 問題的~

ac自動機 則是在 kmp 的 基礎上 用來解決一大串裡面的 許多小串出現次數 出現位置 出現個數 等 問題的

tried樹 + kmp + 融合貫通 = ac自動機

首先ac自動機的建立需要乙個tried樹 然後轉化成tried圖

tried圖 就是 在每個tired樹上的 每個節點的 所有分支(不論存不存在) 連上 一條接向樹上其他節點 的 邊

接向的位置 要連到該字首上一次匹配的點 找最優

tried樹 在這裡打一下 注釋見**

我在這裡 定義 tried圖 為 結構體——

struct tree tr[1 << 20];        // f : 如果 匹配到該位字母 正好失配 應該跳向哪個字串 的 哪個位置 繼續匹配

fa♂q1 這裡 to[26] 是0下標開始 即 a,b......z 通向 to[0],to[1]......to[25]

fa♂q2 我感覺 char 比 string 慢一點? 要用char還是可以的 但別用1下標 即scanf("%s",i + 1);  超慢 應該是頻繁計算+1導致的

fa♂q3 本題是有相同字串的 因此在下面**最後一行是++ 這個要根據題目要求靈活變化

string i;

cin >> i;//讀入要搜尋的字串

pos = 0;//以0為總結點 即第0位字母(不存在)

for (int b = 0 ; b < i.size() ; b ++)//一位一位加入字串裡的字元 注意string型別0下標

++tr[pos].ed;//此時讀完一串了 此時pos是該字串末尾在樹中的位置 因此在此打標記 作為字串的結尾

tried樹建好了 然後自然是tried圖啦 但是怎麼建呢?

我之前貌似說過什麼

"在每個tired樹上的 每個節點的 所有分支(不論存不存在) 連上 一條接向樹上其他節點 的 邊"

right~這裡我們引入佇列que 我這裡用pre代替(天知道我為什麼要用pre這個奇怪的名字)

佇列頭和尾都設為0(為1也沒問題 隨便改改即可) 佇列長度嘛 也要根據空間大小適當合理地調整減他一大半

佇列開始空的 我們如果直接查詢 還要在開始移動隊首時判斷 程式太麻煩 ng

於是乎 我們預處理一下第一層 吶

for (int a = 0 ; a < 26 ; a ++)//此處是查詢是否存在以a到z開頭的字串

if (tr[0].to[a]) pre[++t] = tr[0].to[a];//如果有就把該字元所在字典的位置記錄

這樣 佇列裡就有數了對不對

tip:其實該句if後面應該加上 else tr[0].f = 0 的 但是陣列初始化已經被賦為0了 理解概念時要記住

然後開始拓展查詢 對於存在的拓展的點 需使他的失配節點 匹配到他父親的 通向他那個字母的 失配節點

因為此處佇列的查詢類似bfs 他父親通向他 的失配節點 會比他 的失配節點 早搜尋到

關於這樣為什麼是最優的 你想想你現在匹配了 a 個字元了 然後下乙個匹配不過去 就跳回匹配了 a - 1 個字元的狀態 從那裡的 26 個分支繼續拓展 如果都不行就再退回......這樣就能充分利用公共字首了

對於存在的拓展的點 還需擴充套件隊尾 加入該節點 it's show time~ **就不注釋了看上面幾行

while (h != t)

else tr[p].to[a] = tr[tr[p].f].to[a];

}

自此 tried樹建立完畢 可以開始搜尋啦~

根據題目要求 搜尋這個部分的靈活性很大 本題要求就見放後面的題目鏈結吧(然而我差不多全部部落格用的都是洛谷的模板)

tip: pos 和 tot 此處初始化為 0

for (int a = 0 ; a < i.size() ; a ++)//此處i是總串

(接上)如果再次搜到這裡 便跳出去 還有 本題有重複子串 因此tot是統加

好了 接下來是巨水模板的傳送門

以及貼總** (前面**都是從這裡截的 因此不貼注釋啦)

#include #include #include using namespace std;

struct tree tr[1 << 20];

string i;

int pre[1 << 20];

int n,pos,tot = 0;

int main()

++tr[pos].ed;

} int h = 0,t = 0;

for (int a = 0 ; a < 26 ; a ++)

if (tr[0].to[a])

pre[++t] = tr[0].to[a];

while (h != t)

else tr[p].to[a] = tr[tr[p].f].to[a];

} cin >> i;

pos = tot = 0;

for (int a = 0 ; a < i.size() ; a ++)

printf("%d\n",tot);

return 0;

}

其實也挺短的=-=但是精悍啊~

KMP 模式串匹配演算法

這兩天讀了july的kmp,覺得很受益,寫下以作備忘。kmp最重要的就是求出next陣列,而next陣列則是通過不斷比較 str2 k 與 str2 j 來確定下乙個字元對應的 next數值 相等則直接next j k 不相等則令k next k 進行遞推直到出現 str2 k str2 j 相等的...

串 KMP模式匹配演算法

樸素模式匹配演算法就是簡單的二重迴圈,第一重迴圈主串s從1到n,然後第二重迴圈子串t從1到m進行匹配判斷,時間複雜度為o n m 1 m kmp演算法的核心思想是 第一 如果子串前r個字元均不相等,且子串前r個字元與主串某連續的r個字元匹配,但子串第r 1就不匹配了,則主串的匹配下標可以直接向前跳r...

模式串匹配 KMP演算法

kmp是對字首暴力搜尋的改進,基於的想法其實是很樸素的。首先我們來看一下暴力搜尋。char bf char src,char pattern else if pattern temp 0 return src else return null 如果匹配失敗,則將關鍵字向右滑動乙個字元,從頭開始匹配關...