BZOJ3172: [Tjoi2013]單詞
阿新 • • 發佈:2017-10-05
tin -- mem bzoj3172 構建 bfs 失敗 專題訓練 ble
【傳送門:BZOJ3172】
簡要題意:
給出n個單詞,你可以理解為將這些單詞變成一個個段落,然後求出每個單詞在所有段落中出現的次數
題解:
剛開始不是很懂題目,結果發現將所有單詞看成一篇文章,每個單詞看成一個段落就懂了
由於某種unbelievable的原因,我剛好做了AC自動機的專題訓練,看到這道題就秒想AC自動機
將每個單詞放進AC自動機裏,每個點的s表示有多少個單詞經過,然後在構建失敗指針的時候,通過隊列來更新s值,怎麽更新呢,假設有一個i點,它的失敗指針指向j,設sj為從根到j點所構成的字符串,si為從根到i點所構成的字符串,那麽我們可以知道sj為si的後綴,也說明了si中有sj的出現,那麽就將j點的s值加上i點的s值,達到更新且不重復的目的來求出答案
參考代碼:
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> using namespace std; struct node { int s,c[27],fail; node() { s=fail=0; memset(c,-1,sizeof(c)); } }t[1100000]; int tot,ans,n; char a[1100000]; int ed[210]; void bt(int k,int root) { int x=root,len=strlen(a+1); for(int i=1;i<=len;i++) { int y=a[i]-‘a‘+1; if(t[x].c[y]==-1) { t[x].c[y]=++tot; } x=t[x].c[y]; t[x].s++; } ed[k]=x; } int list[1100000]; void bfs() { int x; int head=1,tail=1; list[1]=0; while(head<=tail) { x=list[head]; for(int i=1;i<=26;i++) { int son=t[x].c[i]; if(son==-1)continue; if(x==0) t[son].fail=0; else { int j=t[x].fail; while(j!=0&&t[j].c[i]==-1) j=t[j].fail; t[son].fail=max(t[j].c[i],0); int x=t[son].fail,y=son; } list[++tail]=son; } head++; } for(int i=tail;i>=1;i--) t[t[list[i]].fail].s+=t[list[i]].s; } int main() { ans=tot=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",a+1); bt(i,0); } bfs(); for(int i=1;i<=n;i++) { printf("%d\n",t[ed[i]].s); } return 0; }
BZOJ3172: [Tjoi2013]單詞