1. 程式人生 > >P3796 【模板】AC自動機(加強版) 題解(Aho-Corasick Automation)

P3796 【模板】AC自動機(加強版) 題解(Aho-Corasick Automation)

指針 題解 d+ [1] 很好 特色 理解 單詞 鏈接

題目鏈接

AC自動機

解題思路

AC自動機模板題。
剛學AC自動機,寫一篇博客增強理解。
AC自動機最關鍵的一點在於,\(fail\)失配指針的構造。
\(fail\)指針指向的地方,是匹配出現錯誤後進行重新匹配的位置,這說明,從根開始到\(fail\)指針指向的地方這一塊字符串,正是我們剛剛失配之前配上的那一塊字符串(子串),且為最長子串。這一點和KMP算法相同。

AC代碼

#include<stdio.h>
#include<string.h>
int ac[100010][26],cnt=1;
int queue[100010],fail[100010],end[100010];
char a[160][75],m[1000010];
struct {
    int num,cnt;
}ans[500],temp;
void push(char a[],int l,int num){//建立trie樹,很好理解不再贅述
    int i,now=0;
    for(i=0;i<l;i++){
        int v=a[i]-‘a‘;
        if(!ac[now][v])ac[now][v]=cnt++;
        now=ac[now][v];
    }
    end[now]=num;
}
void build(){
    int head=0,tail=0,i;//C黨手寫queue
    for(i=0;i<26;i++)if(ac[0][i]){
        queue[tail++]=ac[0][i];//push
        fail[ac[0][i]]=0;
    }
    while(head<tail){
        int v=queue[head++];//pop
        for(i=0;i<26;i++){
            if(ac[v][i]){
                fail[ac[v][i]]=ac[fail[v]][i];
                queue[tail++]=ac[v][i];//push
            }
            else ac[v][i]=ac[fail[v]][i];
            //該節點的失配指針,指向該節點的父節點的失配指針所指向的節點的子節點
            //也即,假設ac[v][i]的父節點失配指針指向的節點為E,則
            //從根節點到E的這個串,為從根節點到v這個串的最長共同結尾子串
            //那麽,ac[v][i]的失配指針應當指向E的第i個節點(保證這一位字符相同)
            //if和else的作用:一個是失配指針,一個是trie圖。
            //如果這點沒有後續了,只能建立trie圖。
            //否則,應當建立失配指針。 
        }
    }
}
void query(char a[],int l){
    int i,j,now=0;
    for(i=0;i<l;i++){
        now=ac[now][a[i]-‘a‘];//沿著建立好的trie圖走
        for(j=now;j;j=fail[j])ans[end[j]].cnt++;//找到單詞末尾並存儲個數
    }
}
//C黨手寫快排
int cmp(int x,int y){
    if(ans[x].cnt>ans[y].cnt)return 1;
    if(ans[x].cnt<ans[y].cnt)return 0;
    if(ans[x].num<ans[y].num)return 1;
    return 0;
}
void qs(int left,int right){
    int i=left,j=right;
    if(i>=j)return;
    while(i!=j){
        for(;i<j;j--)if(cmp(j,left))break;
        for(;i<j;i++)if(cmp(left,i))break;
        if(i!=j){
            temp=ans[i];ans[i]=ans[j];ans[j]=temp;
        }
    }
    j=left;
    temp=ans[i];ans[i]=ans[j];ans[j]=temp;
    qs(left,i-1);
    qs(i+1,right);
}
int main(){
    int i,n;
    while(scanf("%d",&n)){
        if(!n)break;
        cnt=1;
        for(i=1;i<=n;i++){
            scanf("%s",a[i]);
            push(a[i],strlen(a[i]),i);//建立trie樹
            ans[i].num=i;
            ans[i].cnt=0;
        }
        build();//構造AC自動機的fail指針,以及完善trie樹成為trie圖
        scanf("%s",m);
        query(m,strlen(m));//詢問文本串
        //以下為本題特色,不是AC自動機精髓,可跳過
        qs(1,n);
        printf("%d\n",ans[1].cnt);
        printf("%s\n",a[ans[1].num]);
        for(i=2;i<n;i++){
            if(ans[i].cnt-ans[i-1].cnt)break;
            printf("%s\n",a[ans[i].num]);
        }
        memset(end,0,sizeof(int)*cnt);
        memset(fail,0,sizeof(int)*cnt); 
        memset(ac,0,sizeof(ac));    
    }
    return 0;
}

P3796 【模板】AC自動機(加強版) 題解(Aho-Corasick Automation)