模板题和一些坑点:
1.输入的单词可能会有重复的,重复的单词不用重复计算,将相同单词的最小id保存下来就行
2.朴素的AC自动机算法最后一个点会T,用last优化,直接跳到单词末节点,效率提高十分显著。
代码如下:
#include<bits/stdc++.h>//lst优化
#define lowbit(x) ((x)&(-(x)))
#define ll long long
#define INF 0x3f3f3f3f
#define CLR(a) memset(a, 0, sizeof(a))
using namespace std;
const int maxn=1e6+300;
int vis[205],n,mp[205];
struct Trie{
int nxt[maxn][27],fail[maxn],end[maxn],lst[maxn];
int cnt;
void insert(char buf[],int id){
int len=strlen(buf);
int now=0;
for ( int i=0;i<len;i++ ){
int x=buf[i]-'a';
if (!nxt[now][x]) nxt[now][x]=++cnt;
now=nxt[now][x];
}
if(!end[now]) end[now]=id;
mp[id]=end[now];mp[id]记录的是与第k号单词相同的单词的最小编号,这样可以解决重复单词
}
void build(){
queue<int>q;
for (int i=0;i<26;i++) if(nxt[0][i]) q.push(nxt[0][i]);
while (!q.empty()){
int now=q.front();
q.pop();
for ( int i=0;i<26;i++ ){
int x=nxt[now][i];
if (!x)
nxt[now][i]=nxt[fail[now]][i];
else{
fail[x]=nxt[fail[now]][i];
lst[x]=end[fail[x]]?fail[x]:lst[fail[x]];//last优化
q.push(x);
}
}
}
}
void query(char buf[]){
int len=strlen(buf);
int now=0;
int res=0;
for ( int i=0;i<len;i++ ) {
if(buf[i]=='#'){
now=0;continue;
}
now=nxt[now][buf[i]-'a'];
if(end[now]) vis[end[now]]++;
int tmp=lst[now];
while ( tmp ){
vis[end[tmp]]++;
tmp=lst[tmp];
}
}
for(int i=1;i<=n;i++) printf("%d\n",vis[mp[i]]);
}
}ac;
char buf[maxn],s[maxn];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++){
CLR(s);
scanf("%s",s);
ac.insert(s,i);
int len=strlen(s);
s[len]='#';
strcat(buf,s);
}
ac.build();
ac.query(buf);
return 0;
}