[bzoj3172][Tjoi2013]单词——AC自动机

题目大意:

某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。

思路:

第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;
}

posted @ 2018-12-27 15:37  ylsoi  阅读(83)  评论(0编辑  收藏  举报