AC自動機模版代碼解析

   今天還是看AC自動機,終於把模版代碼看懂啦,總結一下:

   AC自動機,應用於模式匹配問題,尤其是長文本多模版問題較於KMP算法和字典樹更具有優勢。方法是把所有模版建成一個大的狀態轉移圖,而不是每個模版各建一個圖。KMP算法的狀態轉移圖是線性的字符串+失配邊組成的,而AC自動機是字典樹+失配邊組成的。字典樹在判斷一個單詞是否屬於字典時很有優勢,但是字典樹在判斷一個文本包含多少個字典中的單詞時就顯得不足了,因爲對於文本串的每次字符都必須從字典樹的跟節點走一遍字典樹(很類似於模式串匹配時用的暴力方法),此時應該用AC自動機。

    模版代碼如下:

    

#include<cstdio>  
#include<cstring>  
#include<queue>  
using namespace std;  
const int maxnode=11000;  
const int sigma_size=26;  
struct AC_Automata  
{  
    int ch[maxnode][sigma_size];  
    int val[maxnode];   // 每個字符串的結尾結點都有一個非0的val  
    int f[maxnode];     // fail函數  
    int last[maxnode];  // last[i]=j表j節點表示的單詞是i節點單詞的後綴,且j節點是單詞節點  
    int sz;  
  
    //初始化0號根節點的相關信息  
    void init()  
    {  
        sz=1;  
        memset(ch[0],0,sizeof(ch[0]));  
        val[0]=0;  
    }  
  
    //insert負責構造ch與val數組  
    //插入字符串,v必須非0表示一個單詞節點  
    void insert(char *s,int v)  
    {  
        int n=strlen(s),u=0;  
        for(int i=0; i<n; i++)  
        {  
            int id=s[i]-'a';  
            if(ch[u][id]==0)  
            {  
                ch[u][id]=sz;  
                memset(ch[sz],0,sizeof(ch[sz]));  
                val[sz++]=0;  
            }  
            u=ch[u][id];  
        }  
        val[u]=v;  
    }  
  
    //getFail函數負責構造f和last數組  
    void getFail()  
    {  
        queue<int> q;  
        last[0]=f[0]=0;  
        for(int i=0; i<sigma_size; i++)  
        {  
            int u=ch[0][i];  
            if(u)  
            {  
                f[u]=last[u]=0;  
                q.push(u);  
            }  
        }  
   /*   然後,就是來看如何利用BFS求出所有結點的失敗指針了.
       1) 對於根結點root的失敗指針,我們將它直接指向NULL,對於根結點下所有的子結點,失敗指針一定是指向root的,因爲當一個字符都不能匹配的時候,自然也就不存在更短的能夠與之匹配的前綴了;
       2) 將求完失敗指針的結點插入隊列中;
       3) 每次彈出一個結點now,詢問它的每個字符對應的子結點,爲了闡述方便,我們將now的i號子結點記爲now->next[i]:
              a) 如果now->next[i]爲NULL,那麼將now->next[i]指向now的失敗指針的i號子結點, 即 now->next[i] = now->fail->next[i];
              b) 如果now->next[i]不等於NULL,則需要構造now->next[i]的失敗指針,由於a)的操作,我們知道now的失敗指針一定存在一個i號子結點,即now->fail->next[i],那麼我們將now->next[i]的失敗指針指向它,即now->next[i]->fail = now->fail->next[i];
       4) 重複2)的操作直到隊列爲空;*/
        while(!q.empty())// 按BFS順序計算fail  
        {  
            int r=q.front(); q.pop();  
            for(int i=0; i<sigma_size; i++)  
            {  
                int u=ch[r][i];  
                if(u==0)continue;  
                q.push(u);  
  
                int v=f[r];  
                while(v && ch[v][i]==0) v=f[v];  
                f[u]= ch[v][i];  
                last[u] =  val[f[u]]?f[u]:last[f[u]];  
            }  
        }  
    }  
  
    //遞歸打印與結點i後綴相同的模版單詞節點編號  
    //進入此函數前需保證val[i]>0  
    void print(int i)  
    {  
        if(i)  
        {  
            printf("%d\n",i);  
            print(last[i]);  
        }  
    }  
  
    // 在s中找出 出現了哪幾個模板單詞  
    void find(char *s)  
    {  
        int n=strlen(s),j=0;  
        for(int i=0; i<n; i++)  
        {  
            int id=s[i]-'a';  
            while(j && ch[j][id]==0) j=f[j];  
            j=ch[j][id];  
            if(val[j]) print(j);  
            else if(last[j]) print(last[j]);  
        }  
    }  
  
};  
AC_Automata ac; 
       明天接着看題目,題目確實很難。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章