首先簡要介紹一下ac自動機:aho-corasick automation,該演算法在2023年產生於貝爾實驗室,是著名的多模匹配演算法之一。乙個常見的例子就是給出n個單詞,再給出一段包含m個字元的文章,讓你找出有多少個單詞在文章裡出現過。要搞懂ac自動機,先得有模式樹(字典樹)trie和kmp模式匹配演算法的基礎知識。ac自動機演算法分為3步:構造一棵trie樹,構造失敗指標和模式匹配過程。
如果你對kmp演算法和了解的話,應該知道kmp演算法中的next函式(shift函式或者fail函式)是幹什麼用的。kmp中我們用兩個指標i和j分別表示,a[i-j+ 1..i]與b[1..j]完全相等。也就是說,i是不斷增加的,隨著i的增加j相應地變化,且j滿足以a[i]結尾的長度為j的字串正好匹配b串的前 j個字元,當a[i+1]≠b[j+1],kmp的策略是調整j的位置(減小j值)使得a[i-j+1..i]與b[1..j]保持匹配且新的b[j+1]恰好與a[i+1]匹配,而next函式恰恰記錄了這個j應該調整到的位置。同樣ac自動機的失敗指標具有同樣的功能,也就是說當我們的模式串在tire上進行匹配時,如果與當前節點的關鍵字不能繼續匹配的時候,就應該去當前節點的失敗指標所指向的節點繼續進行匹配。
看下面這個例子:給定5個單詞:say she shr he her,然後給定乙個字串yasherhs。問一共有多少單詞在這個字串中出現過。我們先規定一下ac自動機所需要的一些資料結構,方便接下去的程式設計。
1const
int kind = 26; 2
struct node
11 }*q[500001];
//佇列,方便用於bfs構造失敗指標
12char keyword[51];
//輸入的單詞
13char str[1000001];
//模式串
14int head,tail;
//佇列的頭尾指標
有了這些資料結構之後,就可以開始程式設計了:
首先,將這5個單詞構造成一棵tire,如圖-1所示。
1void insert(
char *str,node *root)
10 p->count++;
//在單詞的最後乙個節點count+1,代表乙個單詞
11 }
在構造完這棵tire之後,接下去的工作就是構造下失敗指標。構造失敗指標的過程概括起來就一句話:設這個節點上的字母為c,沿著他父親的失敗指標走,直到走到乙個節點,他的兒子中也有字母為c的節點。然後把當前節點的失敗指標指向那個字母也為c的兒子。如果一直走到了root都沒找到,那就把失敗指標指向root。具體操作起來只需要:先把root加入佇列(root的失敗指標指向自己或者null),這以後我們每處理乙個點,就把它的所有兒子加入佇列,隊列為空。
1void build_ac_automation(node *root)
18 p=p->fail;
19 } 20
if(p==null) temp->next[i]->fail=root;
21 }
22 q[head++]=temp->next[i];
23 }
24 }
25 }
26 }
從**觀察下構造失敗指標的流程:對照圖-2來看,首先root的fail指標指向null,然後root入隊,進入迴圈。第1次迴圈的時候,我們需要處理2個節點:root->next[『h』-『a』](節點h) 和 root->next[『s』-『a』](節點s)。把這2個節點的失敗指標指向root,並且先後進入佇列,失敗指標的指向對應圖-2中的(1),(2)兩條虛線;第2次進入迴圈後,從佇列中先彈出h,接下來p指向h節點的fail指標指向的節點,也就是root;進入第13行的迴圈後,p=p->fail也就是p=null,這時退出迴圈,並把節點e的fail指標指向root,對應圖-2中的(3),然後節點e進入佇列;第3次迴圈時,彈出的第乙個節點a的操作與上一步操作的節點e相同,把a的fail指標指向root,對應圖-2中的(4),併入隊;第4次進入迴圈時,彈出節點h(圖中左邊那個),這時操作略有不同。在程式執行到14行時,由於p->next[i]!=null(root有h這個兒子節點,圖中右邊那個),這樣便把左邊那個h節點的失敗指標指向右邊那個root的兒子節點h,對應圖-2中的(5),然後h入隊。以此類推:在迴圈結束後,所有的失敗指標就是圖-2中的這種形式。
最後,我們便可以在ac自動機上查詢模式串中出現過哪些單詞了。匹配過程分兩種情況:(1)當前字元匹配,表示從當前節點沿著樹邊有一條路徑可以到達目標字元,此時只需沿該路徑走向下乙個節點繼續匹配即可,目標字串指標移向下個字元繼續匹配;(2)當前字元不匹配,則去當前節點失敗指標所指向的字元繼續匹配,匹配過程隨著指標指向root結束。重複這2個過程中的任意乙個,直到模式串走到結尾為止。
1int query(node *root)
15 i++;
16 } 17
return cnt;
18 }
對照圖-2,看一下模式匹配這個詳細的流程,其中模式串為yasherhs。對於i=0,1。trie中沒有對應的路徑,故不做任何操作;i=2,3,4時,指標p走到左下節點e。因為節點e的count資訊為1,所以cnt+1,並且講節點e的count值設定為-1,表示改單詞已經出現過了,防止重複計數,最後temp指向e節點的失敗指標所指向的節點繼續查詢,以此類推,最後temp指向root,退出while迴圈,這個過程中count增加了2。表示找到了2個單詞she和he。當i=5時,程式進入第5行,p指向其失敗指標的節點,也就是右邊那個e節點,隨後在第6行指向r節點,r節點的count值為1,從而count+1,迴圈直到temp指向root為止。最後i=6,7時,找不到任何匹配,匹配過程結束。
AC自動機演算法詳解
首先簡要介紹一下ac自動機 aho corasick automation,該演算法在1975年產生於貝爾實驗室,是著名的多模匹配演算法之一。乙個常見的例子就是給出n個單詞,再給出一段包含m個字元的文章,讓你找出有多少個單詞在文章裡出現過。要搞懂ac自動機,先得有模式樹 字典樹 trie和kmp模式...
AC自動機演算法詳解
首先簡要介紹一下ac自動機 aho corasick automation,該演算法在1975年產生於貝爾實驗室,是著名的多模匹配演算法之一。乙個常見的例子就是給出n個單詞,再給出一段包含m個字元的文章,讓你找出有多少個單詞在文章裡出現過。要搞懂ac自動機,先得有模式樹 字典樹 trie和kmp模式...
AC自動機詳解
最近真是太頹了,做了一堆板子題,現在對一些知識點順便來個總結記錄 大家應該都知道kmp和trie樹吧,不懂的可以看我部落格或到網上自己動手尋找資料。ac自動機是乙個很好的東西,這是因為它的名字很好它能夠在有多個模式串的時候進行全文匹配,這十分方便地擴充套件了kmp的功能,實際上它的思路與原理與kmp...