BZOJ3172: [Tjoi2013]单词

时间:2023-12-12 15:17:56

传送门

做了这么多题怎么还是无法很好的理解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;
}