• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            AC自動機算法詳解

                首先簡要介紹一下AC自動機:Aho-Corasick automation,該算法在1975年產(chǎn)生于貝爾實驗室,是著名的多模匹配算法之一。一個常見的例子就是給出n個單詞,再給出一段包含m個字符的文章,讓你找出有多少個單詞在文章里出現(xiàn)過。要搞懂AC自動機,先得有模式樹(字典樹)TrieKMP模式匹配算法的基礎(chǔ)知識。AC自動機算法分為3步:構(gòu)造一棵Trie樹,構(gòu)造失敗指針和模式匹配過程。
                 如果你對KMP算法和了解的話,應(yīng)該知道KMP算法中的next函數(shù)(shift函數(shù)或者fail函數(shù))是干什么用的。KMP中我們用兩個指針ij分別表示,A[i-j+ 1..i]B[1..j]完全相等。也就是說,i是不斷增加的,隨著i的增加j相應(yīng)地變化,且j滿足以A[i]結(jié)尾的長度為j的字符串正好匹配B串的前 j個字符,當A[i+1]B[j+1]KMP的策略是調(diào)整j的位置(減小j值)使得A[i-j+1..i]B[1..j]保持匹配且新的B[j+1]恰好與A[i+1]匹配,而next函數(shù)恰恰記錄了這個j應(yīng)該調(diào)整到的位置。同樣AC自動機的失敗指針具有同樣的功能,也就是說當我們的模式串在Tire上進行匹配時,如果與當前節(jié)點的關(guān)鍵字不能繼續(xù)匹配的時候,就應(yīng)該去當前節(jié)點的失敗指針所指向的節(jié)點繼續(xù)進行匹配。
                  看下面這個例子:給定5個單詞:say she shr he her,然后給定一個字符串yasherhs問一共有多少單詞在這個字符串中出現(xiàn)過。我們先規(guī)定一下AC自動機所需要的一些數(shù)據(jù)結(jié)構(gòu),方便接下去的編程。
             1 const int kind = 26
             2 struct node{  
             3     node *fail;       //失敗指針
             4     node *next[kind]; //Tire每個節(jié)點的個子節(jié)點(最多個字母)
             5     int count;        //是否為該單詞的最后一個節(jié)點
             6     node(){           //構(gòu)造函數(shù)初始化
             7         fail=NULL; 
             8         count=0
             9         memset(next,NULL,sizeof(next)); 
            10     } 
            11 }*q[500001];          //隊列,方便用于bfs構(gòu)造失敗指針
            12 char keyword[51];     //輸入的單詞
            13 char str[1000001];    //模式串
            14 int head,tail;        //隊列的頭尾指針

            有了這些數(shù)據(jù)結(jié)構(gòu)之后,就可以開始編程了:
               
            首先,將這5個單詞構(gòu)造成一棵Tire,如圖-1所示。

             

             1 void insert(char *str,node *root){ 
             2     node *p=root; 
             3     int i=0,index;  
             4     while(str[i]){ 
             5         index=str[i]-'a'
             6         if(p->next[index]==NULL) p->next[index]=new node();  
             7         p=p->next[index];
             8         i++;
             9     } 
            10     p->count++;     //在單詞的最后一個節(jié)點count+1,代表一個單詞
            11 }

            在構(gòu)造完這棵Tire之后,接下去的工作就是構(gòu)造下失敗指針。構(gòu)造失敗指針的過程概括起來就一句話:設(shè)這個節(jié)點上的字母為C,沿著他父親的失敗指針走,直到走到一個節(jié)點,他的兒子中也有字母為C的節(jié)點。然后把當前節(jié)點的失敗指針指向那個字母也為C的兒子。如果一直走到了root都沒找到,那就把失敗指針指向root。具體操作起來只需要:先把root加入隊列(root的失敗指針指向自己或者NULL),這以后我們每處理一個點,就把它的所有兒子加入隊列,隊列為空。

             1 void build_ac_automation(node *root){
             2     int i;
             3     root->fail=NULL; 
             4     q[head++]=root; 
             5     while(head!=tail){ 
             6         node *temp=q[tail++]; 
             7         node *p=NULL; 
             8         for(i=0;i<26;i++){ 
             9             if(temp->next[i]!=NULL){ 
            10                 if(temp==root) temp->next[i]->fail=root;                 
            11                 else
            12                     p=temp->fail; 
            13                     while(p!=NULL){  
            14                         if(p->next[i]!=NULL){ 
            15                             temp->next[i]->fail=p->next[i]; 
            16                             break
            17                         } 
            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 }

                從代碼觀察下構(gòu)造失敗指針的流程:對照圖-2來看,首先rootfail指針指向NULL,然后root入隊,進入循環(huán)。第1次循環(huán)的時候,我們需要處理2個節(jié)點:root->next[‘h’-‘a’](節(jié)點h) root->next[‘s’-‘a’](節(jié)點s)。把這2個節(jié)點的失敗指針指向root,并且先后進入隊列,失敗指針的指向?qū)?yīng)圖-2中的(1)(2)兩條虛線;第2次進入循環(huán)后,從隊列中先彈出h,接下來p指向h節(jié)點的fail指針指向的節(jié)點,也就是root;進入第13行的循環(huán)后,p=p->fail也就是p=NULL,這時退出循環(huán),并把節(jié)點efail指針指向root,對應(yīng)圖-2中的(3),然后節(jié)點e進入隊列;第3次循環(huán)時,彈出的第一個節(jié)點a的操作與上一步操作的節(jié)點e相同,把afail指針指向root,對應(yīng)圖-2中的(4),并入隊;第4次進入循環(huán)時,彈出節(jié)點h(圖中左邊那個),這時操作略有不同。在程序運行到14行時,由于p->next[i]!=NULL(rooth這個兒子節(jié)點,圖中右邊那個),這樣便把左邊那個h節(jié)點的失敗指針指向右邊那個root的兒子節(jié)點h,對應(yīng)圖-2中的(5),然后h入隊。以此類推:在循環(huán)結(jié)束后,所有的失敗指針就是圖-2中的這種形式。

              最后,我們便可以在AC自動機上查找模式串中出現(xiàn)過哪些單詞了。匹配過程分兩種情況:(1)當前字符匹配,表示從當前節(jié)點沿著樹邊有一條路徑可以到達目標字符,此時只需沿該路徑走向下一個節(jié)點繼續(xù)匹配即可,目標字符串指針移向下個字符繼續(xù)匹配;(2)當前字符不匹配,則去當前節(jié)點失敗指針所指向的字符繼續(xù)匹配,匹配過程隨著指針指向root結(jié)束。重復(fù)這2個過程中的任意一個,直到模式串走到結(jié)尾為止。
             1 int query(node *root){ 
             2     int i=0,cnt=0,index,len=strlen(str); 
             3     node *p=root;  
             4     while(str[i]){  
             5         index=str[i]-'a';  
             6         while(p->next[index]==NULL && p!=root) p=p->fail; 
             7         p=p->next[index]; 
             8         p=(p==NULL)?root:p; 
             9         node *temp=p; 
            10         while(temp!=root && temp->count!=-1){ 
            11             cnt+=temp->count; 
            12             temp->count=-1
            13             temp=temp->fail; 
            14         } 
            15         i++;                 
            16     }    
            17     return cnt; 
            18 }
                對照圖-2,看一下模式匹配這個詳細的流程,其中模式串為yasherhs。對于i=0,1。Trie中沒有對應(yīng)的路徑,故不做任何操作;i=2,3,4時,指針p走到左下節(jié)點e。因為節(jié)點e的count信息為1,所以cnt+1,并且講節(jié)點e的count值設(shè)置為-1,表示改單詞已經(jīng)出現(xiàn)過了,防止重復(fù)計數(shù),最后temp指向e節(jié)點的失敗指針所指向的節(jié)點繼續(xù)查找,以此類推,最后temp指向root,退出while循環(huán),這個過程中count增加了2。表示找到了2個單詞she和he。當i=5時,程序進入第5行,p指向其失敗指針的節(jié)點,也就是右邊那個e節(jié)點,隨后在第6行指向r節(jié)點,r節(jié)點的count值為1,從而count+1,循環(huán)直到temp指向root為止。最后i=6,7時,找不到任何匹配,匹配過程結(jié)束。

                到此為止AC自動機算法的詳細過程已經(jīng)全部介紹結(jié)束,看一道例題:http://acm.hdu.edu.cn/showproblem.php?pid=2222
            Problem Description
            In the modern time, Search engine came into the life of everybody like Google, Baidu, etc.
            Wiskey also wants to bring this feature to his image retrieval system.
            Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched.
            To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match.
             

            Input
            First line will contain one integer means how many cases will follow by.
            Each case will contain two integers N means the number of keywords and N keywords follow. (N <= 10000)
            Each keyword will only contains characters 'a'-'z', and the length will be not longer than 50.
            The last line is the description, and the length will be not longer than 1000000.
             

            Output
            Print how many keywords are contained in the description.
             

            Sample Input
            1
            5
            she
            he
            say
            shr
            her
            yasherhs
             

            Sample Output
            3

             1 #include <iostream> 
             2 using namespace std; 
             3   
             4 const int kind = 26
             5 struct node{  
             6     node *fail;       //失敗指針
             7     node *next[kind]; //Tire每個節(jié)點的26個子節(jié)點(最多26個字母)
             8     int count;        //是否為該單詞的最后一個節(jié)點
             9     node(){           //構(gòu)造函數(shù)初始化
            10         fail=NULL; 
            11         count=0
            12         memset(next,NULL,sizeof(next)); 
            13     } 
            14 }*q[500001];          //隊列,方便用于bfs構(gòu)造失敗指針
            15 char keyword[51];     //輸入的單詞
            16 char str[1000001];    //模式串
            17 int head,tail;        //隊列的頭尾指針
            18   
            19 void insert(char *str,node *root){ 
            20     node *p=root; 
            21     int i=0,index;  
            22     while(str[i]){ 
            23         index=str[i]-'a'
            24         if(p->next[index]==NULL) p->next[index]=new node();  
            25         p=p->next[index];
            26         i++;
            27     } 
            28     p->count++
            29 
            30 void build_ac_automation(node *root){
            31     int i;
            32     root->fail=NULL; 
            33     q[head++]=root; 
            34     while(head!=tail){ 
            35         node *temp=q[tail++]; 
            36         node *p=NULL; 
            37         for(i=0;i<26;i++){ 
            38             if(temp->next[i]!=NULL){ 
            39                 if(temp==root) temp->next[i]->fail=root;                 
            40                 else
            41                     p=temp->fail; 
            42                     while(p!=NULL){  
            43                         if(p->next[i]!=NULL){ 
            44                             temp->next[i]->fail=p->next[i]; 
            45                             break
            46                         } 
            47                         p=p->fail; 
            48                     } 
            49                     if(p==NULL) temp->next[i]->fail=root; 
            50                 } 
            51                 q[head++]=temp->next[i];  
            52             } 
            53         }   
            54     } 
            55 
            56 int query(node *root){ 
            57     int i=0,cnt=0,index,len=strlen(str); 
            58     node *p=root;  
            59     while(str[i]){  
            60         index=str[i]-'a';  
            61         while(p->next[index]==NULL && p!=root) p=p->fail; 
            62         p=p->next[index]; 
            63         p=(p==NULL)?root:p; 
            64         node *temp=p; 
            65         while(temp!=root && temp->count!=-1){ 
            66             cnt+=temp->count; 
            67             temp->count=-1
            68             temp=temp->fail; 
            69         } 
            70         i++;                 
            71     }    
            72     return cnt; 
            73 
            74 int main(){ 
            75     int n,t; 
            76     scanf("%d",&t); 
            77     while(t--){  
            78         head=tail=0
            79         node *root=new node(); 
            80         scanf("%d",&n); 
            81         getchar(); 
            82         while(n--){ 
            83             gets(keyword); 
            84             insert(keyword,root); 
            85         } 
            86         build_ac_automation(root); 
            87         scanf("%s",str); 
            88         printf("%d\n",query(root));  
            89     } 
            90     return 0
            91 }

                PS:原創(chuàng),轉(zhuǎn)載請注明出處

            posted on 2009-04-21 18:22 極限定律 閱讀(68417) 評論(40)  編輯 收藏 引用 所屬分類: ACM/ICPC

            評論

            # re: AC自動機算法詳解 2009-06-13 09:53 zab08

            bfs寫成 頭入尾出...

              回復(fù)  更多評論   

            # re: AC自動機算法詳解 2009-07-30 15:39 xtu715

            您好,對于例題,請測試如下樣例:
            1
            2
            tacab
            aca
            wqzpacakkk

            您程序給出的答案是1.
            可根據(jù)題意,正確答案應(yīng)該是2.

            您代碼中65行,貌似有誤。

              回復(fù)  更多評論   

            # re: AC自動機算法詳解 2009-07-30 15:49 xtu715

            是我的錯,請無視。不好意思  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2009-08-25 14:00 zeus

            沒想到又到你這里來 呵呵 看看先  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2009-08-26 15:47 路過

            1
            5
            bhea
            her
            he
            h
            ha
            bhera
            輸出?  回復(fù)  更多評論   

            # re: AC自動機算法詳解[未登錄] 2009-10-16 11:35 DD

            65行的代碼temp->count!=-1這個條件不能要,否則就是錯誤的。  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2010-01-11 21:46 niubi

            是主串在trie上匹配,不是模式串吧。。  回復(fù)  更多評論   

            # re: AC自動機算法詳解[未登錄] 2010-02-26 13:09 intheway

            好東東,樓主辛苦了.  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2010-07-14 00:38 MJ_LC

            @DD
            如果temp->count!=-1說明該節(jié)點和之后的節(jié)點已經(jīng)計算過了  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2010-12-07 22:32 周燎明

            @MJ_LC

            請考慮第66與67 行,考慮將67行加一個執(zhí)行判斷條件:if count==1 再做賦值操作。。。  回復(fù)  更多評論   

            # re: AC自動機算法詳解[未登錄] 2010-12-22 20:03 AC

            失敗指針構(gòu)造的描述好模糊。看了半天還是沒看懂。。繼續(xù)再看  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-03-10 21:44 dereky

            膜拜了  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-04-08 11:19

            你的代碼錯了 失敗函數(shù)寫得有問題  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-05-28 19:45 anmtcel

            您的程序會怎么處理模式串為
            abcd和bc這兩個,
            主串為abcde的這個問題?
            應(yīng)該是兩個串都出現(xiàn)了
            但您的程序應(yīng)該只會找到第一個串,

            因為從a一直順利的走向d,但d的失敗指針直接指向根了,
            也就是不會走到bc那條小路里去。

            我有個想法,就是需不需要預(yù)處理出哪些串是被當前串包含的,
            存當前串所包含的串的位置,也就是當當前串被查找出來后,這些位置也標記為已查找  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-06-19 20:15 fotile

            我只想說,您的代碼實在不漂亮。  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-06-19 20:23 fotile

            講解很淺顯易懂,代碼里一串一串的!=NULL還要很多不必要的判斷實在是配不上文字的高超。  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-07-03 13:46 酷行天下

            大牛講的非常透徹,贊一個……  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-07-31 21:23 aa

            膜拜  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-08-13 14:24 aaa

            各種trie寫成tire  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-08-19 22:15 Evan

            @路過
            應(yīng)該是4 吧。  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-10-21 11:04 mmx

            好!  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2011-12-01 01:58 Miu_Y

            節(jié)點申請了不釋放?  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-02-27 20:55 ITX351

            57行為什么要求長度= = 不理解  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-04-09 17:18 himdd

            char str[1000001];應(yīng)該叫目標串吧。。  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-04-26 09:57 Lucian

            內(nèi)容很棒,很容易理解!  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-05-16 11:25 wzq

            多組數(shù)據(jù)的話,每組做完指針是不是應(yīng)該釋放呢?  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-07-15 23:12 re

            代碼bug超多  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-09-13 12:06 cqwelly

            頂一下  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2012-12-24 18:22 陳文康

            @xtu715
            為啥是2??  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2013-02-17 16:42 h

            temp->count!=-1
            注意:1、重復(fù)的只算一次;2、初始的count=0
            模式串為
            abcd和bc這兩個,
            主串為abcde的這個問題?
            當找到c時,count[c]=0,tmp指針找到串bc的結(jié)尾c并統(tǒng)計。
              回復(fù)  更多評論   

            # re: AC自動機算法詳解 2013-05-02 21:11 xero essential

            其實程序上的錯誤無傷大雅,個人感覺看了第一段就明白原理了,剩下的只是驗證。。  回復(fù)  更多評論   

            # re: AC自動機算法詳解[未登錄] 2013-08-10 21:47 111

            好東西,謝了!  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2014-03-09 10:03 testcc

            不會內(nèi)存泄露么??  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2014-03-25 10:28 Islo

            orz  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2014-07-29 16:28 lb

            先把root加入隊列(root的失敗指針指向自己或者NULL),這以后我們每處理一個點,就把它的所有兒子加入隊列,隊列為空。
            這句話什么意思  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2014-09-12 12:55 Vmurder

            @xtu715
            為什么是2?  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2015-05-10 11:23

            http://sunmoon-template.blogspot.tw/2015/05/aho-corasick-automation-ac.html
            構(gòu)造的地方可以更簡潔  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2015-07-26 19:13 @anyi

            真好  回復(fù)  更多評論   

            # re: AC自動機算法詳解 2015-08-20 11:30 fastmatch

            @anmtcel
            abcd和bc不在一個分支上  回復(fù)  更多評論   

            <2009年4月>
            2930311234
            567891011
            12131415161718
            19202122232425
            262728293012
            3456789

            導(dǎo)航

            統(tǒng)計

            常用鏈接

            留言簿(10)

            隨筆分類

            隨筆檔案

            友情鏈接

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            久久久久久久免费视频| 99久久无码一区人妻a黑| 2021最新久久久视精品爱| 精品久久久无码人妻中文字幕| 国产一区二区三区久久精品| 久久伊人亚洲AV无码网站| 亚洲精品国精品久久99热一| 26uuu久久五月天| 国内高清久久久久久| 国产精品va久久久久久久| 久久婷婷国产剧情内射白浆| 伊人丁香狠狠色综合久久| 久久久久亚洲AV片无码下载蜜桃 | 久久香蕉国产线看观看99| 日本加勒比久久精品| 青青青青久久精品国产| 亚洲AV日韩AV天堂久久| 久久天天躁狠狠躁夜夜2020老熟妇 | 亚洲精品tv久久久久久久久| 久久人人超碰精品CAOPOREN | 狠狠色丁香久久综合五月| 久久人人爽人人爽人人片AV不| 久久这里只有精品久久| 人妻无码中文久久久久专区 | 国产福利电影一区二区三区久久久久成人精品综合 | 久久精品?ⅴ无码中文字幕| 日韩精品久久久肉伦网站| 久久免费看黄a级毛片| 久久久久久免费视频| 久久影视综合亚洲| 日本加勒比久久精品| 久久国产影院| 久久这里有精品视频| 久久er国产精品免费观看8| 国产激情久久久久影院| 久久精品国产亚洲欧美| 久久香蕉一级毛片| 亚洲午夜久久影院| 久久久久亚洲AV成人网| 午夜精品久久久久9999高清| 一级女性全黄久久生活片免费|