P3796 【模板】AC自動機(加強版) 題解(Aho-Corasick Automation)
阿新 • • 發佈:2018-12-07
指針 題解 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)