3172: [Tjoi2013]单词
3172: [Tjoi2013]单词
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 4918 Solved: 2413
[ Submit][ Status][ Discuss]
Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
3
a
aa
aaa
a
aa
aaa
Sample Output
6
3
1
3
1
HINT
Source
首先,这道题肯定是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;
}
好像还挺短的