1. 程式人生 > >BZOJ3172: [Tjoi2013]單詞

BZOJ3172: [Tjoi2013]單詞

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]單詞