[BZOJ3172]:[Tjoi2013]单词(AC自动机)

题目传送门


题目描述:

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


输入格式:

第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N≤200,单词长度不超过106


输出格式:

输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。


样例:

样例输入:

3
a
aa
aaa

样例输出:

6
3
1

题解:

一看是多模式串,首先应该想到是AC自动机。

如果还不会AC自动机,可以转到这篇博客,个人感觉还是写的挺清楚的:AC自动机讲解+[HDU2222]:Keywords Search(AC自动机)

那么我们考虑怎么去处理。

首先,将所有的文字段压入AC自动机,然后搞Fail指针,基本操作,不再赘述。

end数组的含义为:第i个串在第end[i]号点结束,0表示没有串在当前节点结束。

然后在构建Fail指针的时候,将每个点的BFS序压入队列,每个点的答案只有可能由它上面的点转移而来,所以按此顺序统计不会出错。

最后输出每个串的结尾点统计的答案即可。


代码时刻:

#include<bits/stdc++.h>
using namespace std;
int n;
int cnt=1,nxt[1000001],trie[1000001][30],end[1000001],que[1000001];
int flag[1000001],ans[1000001],sum=1;
char s[2000001];
void insert(char *str,int id)//构建Trie树
{
	int len=strlen(str);
	int p=1;
	for(int i=0;i<len;i++)
	{
		int ch=str[i]-'a';
		if(!trie[p][ch])trie[p][ch]=++cnt;
		ans[p=trie[p][ch]]++;//每个点被经过的次数统计
	}
	end[id]=p;//第i个串在第end[i]号点结束
}
void build()
{
	for(int i=0;i<26;i++)trie[0][i]=1;
	que[1]=1;
	int head=1,tail=1;
	while(head<=tail)
	{
		flag[++sum]=que[head];//BFS序
		for(int i=0;i<26;i++)
		{
			if(!trie[que[head]][i])trie[que[head]][i]=trie[nxt[que[head]]][i];
			else
			{
				que[++tail]=trie[que[head]][i];
				nxt[trie[que[head]][i]]=trie[nxt[que[head]]][i];
			}
		}
		head++;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s);
		insert(s,i);
	}
	build();
	for(int i=sum;i>0;i--)ans[nxt[flag[i]]]+=ans[flag[i]];//统计答案
	for(int i=1;i<=n;i++)cout<<ans[end[i]]<<endl;//输出每个点结束位置的答案
	return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11081471.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值