做了这么多题怎么还是无法很好的理解AC自动机呢..果然是个制杖
首先题意表述不是很清晰,这些所有的单词组成了那个文章,所以果断建个AC自动机,建的时候给每个点附加一个权值,建树是经过一次权值即+1。
然后根据建立fail的轨迹,其实就是Trie树的bfs序。根据这个逆序。对于每个点fail指向的点,把那个店的权值累加上当前点的权值。然后输出就行了。
建树的时候初始累加的权值也就是整篇文章有多少个独立的单词,然后每个fail指针指向词一定是在当前的词里出现过。所以就把被指向的点的权值加上当前点的权值。
//BZOJ 3172 //by Cydiater //2016.10.21 #include <iostream> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <ctime> #include <cmath> #include <iomanip> #include <cstdlib> #include <cstdio> using namespace std; #define ll long long #define up(i,j,n) for(int i=j;i<=n;i++) #define down(i,j,n) for(int i=j;i>=n;i--) const int MAXN=1e6+5; const int oo=0x3f3f3f3f; inline int read(){ char ch=getchar();int x=0,f=1; while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } char s[MAXN]; int sum[MAXN],next[MAXN][26],fail[MAXN],N,LEN,cnt=0,now,end[MAXN],q[MAXN],head,tail; namespace solution{ void insert(){ now=0; up(i,1,LEN){ if(!next[now][s[i]-'a'])next[now][s[i]-'a']=++cnt; now=next[now][s[i]-'a'];sum[now]++; } } void init(){ N=read(); up(i,1,N){ scanf("%s",s+1); LEN=strlen(s+1); insert();end[i]=now; } } void buildAC(){ head=1;tail=0;up(i,0,25)if(next[0][i])q[++tail]=next[0][i]; for(;head<=tail;head++){ int node=q[head]; up(i,0,25){ int son=next[node][i]; if(!son)next[node][i]=next[fail[node]][i]; else{ fail[son]=next[fail[node]][i]; q[++tail]=son; } } } } void slove(){ down(head,tail,1)sum[fail[q[head]]]+=sum[q[head]]; } void output(){ up(i,1,N)printf("%d\n",sum[end[i]]); } } int main(){ //freopen("input.in","r",stdin); using namespace solution; init(); buildAC(); slove(); output(); return 0; }