Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
3
a
aa
aaa
Sample Output
6
3
1
思路:
这个题是问你一个串在所有串中出现的次数。。
首先我们理解一下fail指针,如果b串中有一个字母的fail指针指向a串末尾,那么a 串一定存在于b串中,
而且我们知道,fail指针指向的值一定是越变越小的,最后指向根节点0.
我们可以在建树的的时候,在每个单词出现的字母的位置都加1,代表这个单词,或者这个单词的前缀出现过。
最后 获取 fail指针的时候,记录一下队列顺序,
在把当前节点的值,加入到当前节点的fail指针指向的节点中去。fail指针不断向上走,所以保证了我们结果的正确性。
#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x))
#define go(i,a,b) for (int i = a; i <= b; i++)
#define og(i,a,b) for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 5e5+10;
int Ac[N][27],Fail[N],Num[N];
int q[N],id[N],cnt,w[N];
char s[N];
void Build(int k){
int now = 0;
int len = strlen(s);
go(i,0,len-1){
if (!Ac[now][s[i]-'a']) Ac[now][s[i]-'a'] = ++cnt;
Num[now = Ac[now][s[i]-'a']]++;
}
id[k] = now;
}
void Get_fail(){
int l = 0,r = 0;
go(i,0,25) if (Ac[0][i]) q[r++] = Ac[0][i];
while(l < r){
int u = q[l++];
go(i,0,25){
if (Ac[u][i]) {
Fail[Ac[u][i]] = Ac[Fail[u]][i];
q[r++] = Ac[u][i];
} else Ac[u][i] = Ac[Fail[u]][i];
}
}
og(i,l-1,0) Num[Fail[q[i]]] += Num[q[i]];
}
int main(){
int n;
scanf("%d",&n);
go(i,1,n){
scanf("%s",s);
Build(i);
}
Get_fail();
go(i,1,n) printf("%d\n",Num[id[i]]);
return 0;
}