• <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>

            icecream

            我的代碼備份!

            AC 自動機算法詳解(轉)

            首先簡要介紹一下AC自動機:Aho-Corasick automation,該算法在1975年產生于貝爾實驗室,是著名的多模匹配算法之一。一個常見的例子就是給出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自動機所需要的一些數據結構,方便接下去的編程。
            1 const int kind = 26;
            2 struct node{  
            3      node *fail;       //失敗指針
            4      node *next[kind]; //Tire每個節點的個子節點(最多個字母)
            5     int count;        //是否為該單詞的最后一個節點
            6      node(){           //構造函數初始化
            7          fail=NULL;
            8          count=0;
            9          memset(next,NULL,sizeof(next));
            10      }
            11 }*q[500001];          //隊列,方便用于bfs構造失敗指針
            12 char keyword[51];     //輸入的單詞
            13 char str[1000001];    //模式串
            14 int head,tail;        //隊列的頭尾指針

            有了這些數據結構之后,就可以開始編程了:
                首先,將這5個單詞構造成一棵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++;     //在單詞的最后一個節點count+1,代表一個單詞
            11 }

            在構造完這棵Tire之后,接下去的工作就是構造下失敗指針。構造失敗指針的過程概括起來就一句話:設這個節點上的字母為C,沿著他父親的失敗指針走,直到走到一個節點,他的兒子中也有字母為C的節點。然后把當前節點的失敗指針指向那個字母也為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 }

                從代碼觀察下構造失敗指針的流程:對照圖-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個過程中的任意一個,直到模式串走到結尾為止。
            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中沒有對應的路徑,故不做任何操作;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時,找不到任何匹配,匹配過程結束。

            posted on 2009-04-28 19:31 icecream 閱讀(413) 評論(0)  編輯 收藏 引用 所屬分類: String

            My Links

            Blog Stats

            常用鏈接

            留言簿

            隨筆分類

            隨筆檔案

            文章分類

            ACMER

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            久久亚洲视频| 99久久免费只有精品国产| 精品久久久久成人码免费动漫| 久久久噜噜噜久久中文字幕色伊伊| 国产精品激情综合久久| 99久久精品免费看国产免费| 日韩va亚洲va欧美va久久| 色综合久久中文字幕无码| 国产亚州精品女人久久久久久| 热久久视久久精品18| 嫩草影院久久国产精品| 久久久www免费人成精品| 国产视频久久| 99久久婷婷免费国产综合精品| 久久激情五月丁香伊人| 99久久人妻无码精品系列蜜桃| 久久久国产精品| 青青草国产精品久久久久| 77777亚洲午夜久久多喷| 99精品久久久久久久婷婷| 麻豆一区二区99久久久久| 三级韩国一区久久二区综合| 国产一区二区精品久久| 久久99国内精品自在现线| 影音先锋女人AV鲁色资源网久久 | 久久99热精品| 伊人久久五月天| 国内精品久久国产| 色天使久久综合网天天| 久久婷婷人人澡人人| 精品久久久久久国产免费了| 久久久久国产精品| 色综合久久中文色婷婷| 久久噜噜电影你懂的| 久久99精品国产99久久| 色综合久久中文色婷婷| 国产亚洲成人久久| 亚洲精品无码久久不卡| 久久无码AV中文出轨人妻| 亚洲中文字幕无码久久2017| 伊人久久大香线蕉av不卡|