[bzoj3172][Tjoi2013]單詞——AC自動機
阿新 • • 發佈:2018-12-27
一個 dig debug 自動機 getc 前綴 rst cto space
題目大意:
某人讀論文,一篇論文是由許多單詞組成。但他發現一個單詞會在論文中出現很多次,現在想知道每個單詞分別在論文中出現多少次。
思路:
第i個單詞在整個文章中出現了多少次即i串的結尾可以被多少個串的節點給跳到。
於是吧fail看成每個節點唯一的父親,每個節點的權值為有多少個單詞的前綴經過了它,然後直接統計子樹內的權值和即可。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define debug(x) cout<<#x<<"="<<x<<" " #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; using namespace std; void File(){ freopen("bzoj3172.in","r",stdin); freopen("bzoj3172.out","w",stdout); } template<typename T>void read(T &_){ _=0; T f=1; char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0'); _*=f; } const int maxn=1e6+10; int n,cnt_rank,ans[maxn],weight[maxn]; int ch[maxn][26],num[maxn],fail[maxn],cnt=1; vector<int>rank[maxn]; void insert(char *s){ int len=strlen(s+1),u=1,c; REP(i,1,len){ c=s[i]-'a'; if(!ch[u][c])ch[u][c]=++cnt; u=ch[u][c]; ++weight[u]; } ++num[u]; rank[u].pb(++cnt_rank); } void build_fail(){ int h=1,t=0,q[maxn]; fail[1]=1; REP(i,0,25){ int v=ch[1][i]; if(v)q[++t]=v; if(v)fail[v]=1; else ch[1][i]=1; } while(h<=t){ int u=q[h++]; REP(i,0,25){ int v=ch[u][i]; if(v)q[++t]=v; if(v)fail[v]=max(ch[fail[u]][i],1); else ch[u][i]=max(ch[fail[u]][i],1); } } } int beg[maxn],to[maxn],las[maxn],cnte=1; int sz[maxn]; void add(int u,int v){ las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; } void dfs(int u){ sz[u]=weight[u]; for(int i=beg[u];i;i=las[i]){ dfs(to[i]); sz[u]+=sz[to[i]]; } REP(i,0,rank[u].size()-1)ans[rank[u][i]]=sz[u]; } int main(){ File(); char s[maxn]; read(n); REP(i,1,n){ scanf("%s",s+1); //printf("%s\n",s+1); insert(s); } build_fail(); REP(i,2,cnt)add(fail[i],i); dfs(1); REP(i,1,n)printf("%d\n",ans[i]); return 0; }
[bzoj3172][Tjoi2013]單詞——AC自動機