题目大意
%
求
n
n
n 个模式串
T
i
T_i
Ti 在文本串
S
S
S 中分别出现的次数。
数据范围
1
⩽
n
⩽
2
×
1
0
5
,
∑
∣
T
i
∣
⩽
2
×
1
0
5
,
∣
S
∣
⩽
2
×
1
0
6
1\leqslant n\leqslant 2\times 10^5,\sum|T_i|\leqslant 2\times 10^5,|S|\leqslant 2\times 10^6
1⩽n⩽2×105,∑∣Ti∣⩽2×105,∣S∣⩽2×106
题解
%
建立AC自动机,构建Fail树,对于每个模式串的询问映射到树上表示该状态的点(代码中的id数组),计算答案时在Fail树上差分,统计答案按Fail树的拓扑序合并子节点即可。
其实不必构建出Fail树,更不必进行拓扑排序,可以发现,构建Fail指针的时入队列的倒序即为其中一种拓扑序。因而我们只需要直接统计即可。
#include<bits/stdc++.h>
using namespace std;
#define maxk 26
#define maxn 200010
struct node{
node *ch[maxk],*fail; int cnt;
void clear(){memset(this,0,sizeof *this);}
};
node *id[maxn];
struct AC{
static const int maxl=1000010;
node nodes[maxl],*q[maxl],*cur,*root,*sroot;
node *newnode(){
cur->clear();
return cur++;
}
void clear(){
cur=nodes;
root=newnode();
sroot=newnode();
for(int i=0;i<maxk;i++)
sroot->ch[i]=root;
root->fail=sroot;
}
void insert(const char *s,int d){
node *t=root;
for(;*s;++s){
int x=*s-'a';
if(t->ch[x]==NULL)
t->ch[x]=newnode();
t=t->ch[x];
} id[d]=t;
}
int ed;
void build(){
int st=0;ed=0;
q[ed++]=root;
while(st!=ed){
node *t=q[st++];
for(int i=0;i<maxk;i++)
if(t->ch[i]) t->ch[i]->fail=t->fail->ch[i],q[ed++]=t->ch[i];
else t->ch[i]=t->fail->ch[i];
}
} void run(const char *);
}ac;
int n;
char str[2000010];
void AC::run(const char *s){
for(node *t=root;*s;s++)
t=t->ch[*s-'a'],++t->cnt;
for(int i=ed-1;i>=0;i--)
q[i]->fail->cnt+=q[i]->cnt;
for(int i=1;i<=n;i++)
printf("%d\n",id[i]->cnt);
}
int main(){
ac.clear();
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%s",str),ac.insert(str,i);
ac.build();
scanf("%s",str);
ac.run(str);
return 0;
}