AC自動機的實現原理

來源:互聯網
上載者:User

       最近學習AC自動機,看了不少講解AC自動機的文章,幾乎都是在講如何操作。估計不少人學習時像我一樣在想AC自動機演算法為什麼能實現多模式串匹配操作。如下是我的思考成果,如有漏洞,歡迎指正。

       建立trie樹比較容易,構造fail指標其實是同樣的匹配過程,只要理解query()也就都明白了,下面主要來說說query()是如何完整地尋找出所有的模式串的。

       對於給定的長字串,尋找有多少模式串在裡面出現過。query函數依次讀入長字串裡的字元,而匹配某一模式串操作是在query函數讀入長字串的某一字元時發生的。query依次讀入字元,不受其他動作影響(無論有沒有發生匹配query都老老實實地一個接一個地讀串)。每讀入長字串中的一個字元str[i],便需要求這樣一個子串,設為s[i](即str(k...i)串),滿足如下關係:

                       該子串是某一模式串的首碼,且是所有模式串中的最長首碼, 即不存在某字串的首碼為str(k1...i),k1<k。以下滿足該關係的子 串均簡稱為最長首碼(注意指的是模式串的最長首碼,不要混淆)。

       現在要做的是每讀入一個str[i],求出它的最長首碼(s[i])。以下是求s[i]的方法:

s[i]=s[i-1]+str[i]-------------------->>>>當前配對的模式串的下一個字元==str[i](顯然s[i-1]表示上一狀態即掃描到str[i-1]的最長首碼);

s[i]=houzhui(houzhui...(houzhui(s(i-1))))+str[i]---------------->>>>當前配對的模式串的下一個字元!=str[i]

(houzhui()函數取的串滿足如下條件1當前配對串的尾碼;2其他模式串首碼;3滿足條件1、2的最長的串。描述起來很費勁,但你觀察已經建立好的trie樹和fail指標的特性,發現所謂houzhui()操作是水到渠成的(思考一下,其實trie樹有很多隱含特性的)。僅需第75、76行代碼。顯然,取尾碼操作是有截止條件的,截止條件就是當取得的尾碼(設為L(j...k))是某字串(設為L1(1...m))的首碼,且元素L1(k-j+1)==str[i],那麼L(j...k)+str[i]便是我們所求的s[i]。)

       這個看起來像不像DP中的遞推關係式?把每一步求解str[i]看做一個狀態,每一步str[i]最長首碼s[i]的求解依賴於上一狀態str[i-1]的解,這應該就是AC自動機中的DP思想。

       求解出每一狀態的最長首碼還遠沒有結束,現在我們知道了目前狀態下(即當前str[i]下)的最長首碼和str[i]這個字元,要求的是在這個str[i]下發生匹配的模式串。現在做如下討論:

                      若某一模式串在str[i]狀態下被匹配,則該模式串的末尾字元==str[i],且之前的字元是str[i]的最長首碼的某一尾碼。

       下面就尋找滿足上述關係的模式串進行匹配操作,也就是依次尋找str[i]下的最長首碼的最長尾碼,該操作便是(之前看講解對此都是含糊不清的)。

while(temp!=root&&temp->count!=-1){cnt+=temp->count;temp->count=-1;temp=temp->fail;}

       看是否有模式串恰好是那個str[i]下的最長首碼的尾碼。若有,則該串被匹配,不要停,繼續找尾碼,直到尾碼為0,即fail指向了root。(至於為什麼繼續找應該不需要解釋了)此處尋找操作再次用到了fail指標,fail指標的作用就是協助我們找尾碼,當然,準確地說是同樣出現在模式串中的尾碼。

貼出代碼(hdu2222):

#include<iostream>#include<cstring>#include<cstdio>using namespace std;struct node{node *fail;node *next[26];int count;}*q[500001];   //用作建trie樹時廣搜的隊列char keyword[51];char str[1000001];int head,tail;void insert(char str[],node *root){node *p=root;int i=0,cur;while(str[i]){cur=str[i]-'a';if(p->next[cur]==NULL)p->next[cur]=new node();p=p->next[cur];i++;}p->count++;}void build(node *root){int i;root->fail=NULL;q[head++]=root;  //廣搜隊列while(head!=tail){node *temp=q[tail++];node *p=NULL;for(i=0;i<26;i++){if(temp->next[i]!=NULL){if(temp==root)temp->next[i]->fail=root;else{p=temp->fail;while(p!=NULL){if(p->next[i]!=NULL){temp->next[i]->fail=p->next[i];break;}p=p->fail;}if(p==NULL)temp->next[i]->fail=root;}q[head++]=temp->next[i];   //入隊列}}}}int query(node *root){int i=0,cnt=0,cur;node *p=root;while(str[i]){cur=str[i]-'a';while(p->next[cur]==NULL&&p!=root)p=p->fail;p=p->next[cur];p=(p==NULL)?root:p;node *temp=p;while(temp!=root&&temp->count!=-1){   //此操作見上述講解cnt+=temp->count;temp->count=-1;temp=temp->fail;}i++;}return cnt;}int main(){int t,n;scanf("%d",&t);while(t--){head=tail=0;node *root=new node();scanf("%d",&n);while(n--){scanf("%s",keyword);insert(keyword,root);}build(root);scanf("%s",str);printf("%d\n",query(root));}return 0;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.