解题思路
某字符串在论文中出现多少次=某字符串在每个单词中出现的次数之和
跑一边
A
C
AC
AC自动机,设
s
u
m
[
i
]
sum[i]
sum[i]表示节点i到根节点的字符串是多少个单词的子串,所以初始时将插入字符时,将途中经过的节点
s
u
m
[
i
]
+
+
sum[i]++
sum[i]++。
知道
f
a
i
l
[
i
]
fail[i]
fail[i]到根形成的字符串也一定是那个单词的子串
s
u
m
[
i
]
+
=
s
u
m
[
f
a
i
l
[
i
]
]
sum[i]+=sum[fail[i]]
sum[i]+=sum[fail[i]]
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;
int n,tot=1,t,sum1[1001000],cnt[1001000],pos[1001000];
char s[1001000];
struct c {
int fail,v;
int son[30];
} trie[1001000];
void insert(char s[],int ii) {
int len=strlen(s+1),p=0;
for(int i=1; i<=len; i++) {
char c=s[i]-'a';
if(!trie[p].son[c])
trie[p].son[c]=++tot;
p=trie[p].son[c];
sum1[p]++;
}
pos[ii]=p;
return;
}
void getfail() {
queue<int>q;
for(int i=0; i<26; i++) {
int c=trie[0].son[i];
if(c) {
trie[c].fail=0;
cnt[++t]=c;
q.push(c);
}
}
while(!q.empty()) {
int x=q.front();
q.pop();
int f=trie[x].fail;
for(int i=0; i<26; i++) {
int c=trie[x].son[i];
if(c) {
trie[c].fail=trie[f].son[i];
cnt[++t]=c;
q.push(c);
} else trie[x].son[i]=trie[f].son[i];
}
}
for(int i=tot;i^1;--i)
sum1[trie[cnt[i]].fail]+=sum1[cnt[i]];
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%s",s+1);
insert(s,i);
}
getfail();
for(int i=1;i<=n;i++)
{
printf("%d\n",sum1[pos[i]]);
}
}