部落格《模式串匹配: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 如果匹配失敗,則將關鍵字向右滑動乙個字元,從頭開始匹配關...