AC自動機精講

http://www.cnblogs.com/Booble/archive/2010/12/05/1897121.html

//hdu2222
//ACauto
//構造失敗指針:設當前節點上的字母爲C,沿着他父親的失敗指針走,直到走到一個節點,他的兒子中也有字母爲C的。然後把當前節點的失敗指針指向那個字母也爲C的兒子。如果一直走到了root都沒找到,那就把失敗指針指向root。
//匹配(1)當前字符匹配,只需沿該路徑走向下一個節點繼續匹配即可;(2)當前字符不匹配,則去當前節點失敗指針所指向的字符繼續匹配.重複這2個過程中的一個,直到模式串走完。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#define cha 26
#define Root 0
#define N 500001
using namespace std;
struct node{
    int data;//結點信息
    int count;//從根到此處是否是關鍵字,並且記錄是多少個關鍵字的結尾
    int fail;
    int next[cha];
}tree[N];

void init(node &a,int data){
    a.data = data;
    a.count = 0;
    a.fail = Root;
    for(int i=0;i<cha;i++)
        a.next[i] = -1;
}

int k = 1;
void Insert(char s[]){
    int p = Root;
    for(int i=0;s[i];i++){
        int data = s[i]-'a';
        if(tree[p].next[data]==-1){//不存在該結點
            init(tree[k],data);
            tree[p].next[data] = k;
            k++;
        }
        p = tree[p].next[data];
    }
    tree[p].count++;
}

queue<node> q;
void AC_automation(){
    q.push(tree[Root]);
    while(!q.empty()){
        node k = q.front();
        q.pop();
        for(int j=0; j<cha; j++){
            if( k.next[j]!=-1 ){
                if( k.data == -1 ) tree[k.next[j]].fail = Root;
                else{
                    int t = k.fail;
                    while( t!=Root && tree[t].next[j]==-1 ) t = tree[t].fail;
                    tree[ k.next[j] ].fail = max( tree[t].next[j], Root );
                }
                q.push(tree[k.next[j]]);
            }
        }
    }
}

int get_ans(char s[]){
    int k=Root, ans = 0;
    for(int i=0;s[i];i++){
        int t = s[i] - 'a';
        while(tree[k].next[t]==-1 && k ) k = tree[k].fail;
        k = tree[k].next[t];
        if(k==-1){ k = Root;continue;}
        int j = k;
        while( tree[j].count ){
            ans += tree[j].count;
            tree[j].count = 0;
            j = tree[j].fail;
        }
        //下面兩句很重要,如果走到頭以後當前字母不是關鍵字終點然而其fail指針指向字母是關鍵字終點的話,
        //應當加入此關鍵值,而網上大多數程序忽視了這一點導致hdu的discuss裏反例過不了
        ans += tree[ tree[j].fail ].count;
        tree[ tree[j].fail ].count = 0;
    }
    return ans;
}

char tar[2*N];
int main(){
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init(tree[Root],-1);
        char a[55];
        while(n--){
            scanf("%s",a);
            Insert(a);
        }
        AC_automation();
        scanf("%s",tar);
        printf("%d\n",get_ans(tar));
    }
    return 0; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章