bzoj3172【TJOI2013】单词

3172: [Tjoi2013]单词

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 2178   Solved: 1012
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

3
a
aa
aaa

Sample Output

6
3
1

HINT

Source



AC自动机+递推,思路很好(详见程序)




#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define pa pair<int,int>
#define maxn 1000005
#define inf 1000000000
using namespace std;
int n,tot=1,cnt=0,t[maxn][26],go[maxn],w[maxn],a[maxn],p[205];
bool v[maxn];
char s[maxn];
queue<int> q;
inline void bfs()
{
	q.push(1);
	while (!q.empty())
	{
		int x=q.front(),y,j;q.pop();v[x]|=v[go[x]];
		a[++cnt]=x;//a数组是按bfs顺序生成的 
		F(i,0,25)
		{
			j=go[x];
			while (j&&!t[j][i]) j=go[j];
			if (t[x][i])
			{
				go[y=t[x][i]]=j?t[j][i]:1;
				q.push(y);
			}
			else t[x][i]=j?t[j][i]:1;
		}
	}
}
int main()
{
	scanf("%d",&n);
	F(i,1,n)
	{
		scanf("%s",s);
		int len=strlen(s),now=1;
		F(j,0,len-1)
		{
			int x=s[j]-'a';
			if (!t[now][x]) t[now][x]=++tot;
			now=t[now][x];
			w[now]++;
		}
		p[i]=now;//p数组记录了每个单词结尾节点的位置 
	}
	bfs();
	D(i,cnt,1) w[go[a[i]]]+=w[a[i]];//这里计算答案的方法很巧妙,相当于按bfs的反方向递推 
	F(i,1,n) printf("%d\n",w[p[i]]);//这里只要输出w[p[i]]就可以了 
	return 0;
}


你可能感兴趣的:(AC自动机,bzoj)