3172: [Tjoi2013]单词

3172: [Tjoi2013]单词

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 4918   Solved: 2413
[ 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

[ Submit][ Status][ Discuss]

 首先,这道题肯定是AC自动机,嗯……

一开始我是用十分暴力的方法,把AC自动机建好后把每个数都带进去跑一遍,然后就GG了。

这道题的关键是建fail(next)树,,这样就可以把题目巧妙地转换为求子树和,嗯。

还是挺简单的,这里安利一篇博客(不是我写的,我的水平不行)

上代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1000005;
const int C = 26;
vector<int>a[N];
int pos[N],ch[N][C],ans[N],cnt[N],tot,nxt[N],n,t;
char st[205][N];
void init(){//多组数据用来清空
	memset(ch,0,sizeof(ch));
	memset(cnt,0,sizeof(cnt));
	memset(nxt,0,sizeof(nxt));
	tot=0;
}
void insert(int x,int id){
	int p=0;
	for(int i=0;i<x;i++){
		if(!ch[p][st[id][i]-'a']) ch[p][st[id][i]-'a']=++tot;
		p=ch[p][st[id][i]-'a']; cnt[p]++;//注意,这里要每个都加,因为是求单词出现的次数(可以画图理解一下)
	}
	pos[id]=p;//小小的映射
}
void build(){//建树不多说
	queue<int>q;
	int p=0;
	for(int i=0;i<C;i++){
		if(ch[p][i]) q.push(ch[p][i]);
	}
	while(!q.empty()){
		p=q.front();q.pop();
		for(int i=0;i<C;i++){
			if(ch[p][i]){
				q.push(ch[p][i]);
				nxt[ch[p][i]]=ch[nxt[p]][i];
			}else ch[p][i]=ch[nxt[p]][i];	
		}
	}
}
void dfs(int x,int fa){//求子树和
	for(int i=0;i<a[x].size();i++){
		if(a[x][i]!=fa){
			dfs(a[x][i],x);
			cnt[x]+=cnt[a[x][i]];
		}
	}
}
int main(){
	scanf("%d",&n);
	        for(int i=1;i<=n;i++){
			scanf("%s",st[i]);
			insert(strlen(st[i]),i);
		} 
		build();
		for(int i=1;i<=tot;i++) a[nxt[i]].push_back(i);//建fail(next)树
		dfs(0,-1); 
		for(int i=1;i<=n;i++){
			printf("%d\n",cnt[pos[i]]); //输出(映射)
		}
	return 0;
}
好像还挺短的

猜你喜欢

转载自blog.csdn.net/qq_38944163/article/details/81026267