AC自動機演算法詳解

2021-06-25 22:05:34 字數 2907 閱讀 8570

首先簡要介紹一下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...