分析:
对个字符串跑一边AC自动机,建立fail树(即由fail指针组成的树),每个点的点权为在Trie树上经过这个点的字符串的数量。则每个点所代表的的字符串在所有单词中出现次数显然为这个点在fail树上的子树的点权和,跑一遍树形DP,记录一下每个单词的位置输出即可。
具体看代码
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 220,maxm = 1e6 + 50;
int n,cnt,l,r,a[maxn],f[maxm],nxt[maxm],t[maxm][26],q[maxm];
char s[maxm];
void insert(char *s,int x){
int now = 0,len = strlen(s);
for(int i = 0; i < len; i ++){
if(!t[now][s[i] - 'a']) t[now][s[i] - 'a'] = ++cnt;
now = t[now][s[i] - 'a'],f[now] ++;//经过字符串加一
}
a[x] = now;//记录每个字符串位置
}
void build(){
l = 1,r = 0;
for(int i = 0; i < 26; i ++) if(t[0][i]) nxt[t[0][i]] = 0,r ++,q[r] = t[0][i];
while(l <= r){
int now = q[l];
l ++;
for(int i = 0; i < 26; i ++){
if(t[now][i]) nxt[t[now][i]] = t[nxt[now]][i],r ++,q[r] = t[now][i];
else t[now][i] = t[nxt[now]][i];
}
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; i ++){
scanf("%s",s);
insert(s,i);
}
build();
for(int i = r; i >= 1; i --) f[nxt[q[i]]] += f[q[i]];//由于建树时相当于已经把点在队列里按深度排序,可以直接循环,不需要dfs
for(int i = 1; i <= n; i ++) printf("%d\n",f[a[i]]);
return 0;
}
来源:CSDN
作者:weixin_45429627
链接:https://blog.csdn.net/weixin_45429627/article/details/104165644