给我 1 个主串 S 和 n 个模式串 T,问对于每个模式串 T,在 S 中出现的次数是多少,要求出现的 T 串不能相交
思路
在 ac 自动机的基础上添加一个 last [] 数组,对于 trie 树的节点 x,last [x] 表示以 x 结尾的前缀串的上一次出现的位置, 2. 又 ac 自动机的基础上添加一个 dep [] 数组,dep [x] 表示以 x 节点的结尾的前缀的长度
通过字符串两次出现的长度差值
i
−
l
a
s
t
[
x
]
>
=
d
e
p
[
x
]
i-last[x] >=dep[x]
i−last[x]>=dep[x] 来得到的,当前这次,和之前出现的拿出是否重叠,没有重叠的话贡献 ++。
ac自动机 代码
#include<bits/stdc++.h>
using namespace std;constint N =5e5+10;int n;int tr[N][26], fa[N], id[N], idx;int last[N], dep[N];char s[N], t[50];int ans[N];voidinit(){memset(tr[0],0,sizeof tr[0]);//多组特殊的初始化别忘了memset(last,-1,sizeof last);memset(ans,0,sizeof ans);
idx =0;}intnewnode(){
idx ++;for(int i =0; i <26; i ++) tr[idx][i]=0;return idx;}voidinsert(char s[],int k){int u =0;for(int i =0; s[i]; i ++){if(tr[u][s[i]-'a']==0){
tr[u][s[i]-'a']=newnode();
dep[idx]= i +1;}
u = tr[u][s[i]-'a'];}
id[k]= u;}voidbuild(){
queue<int> q;for(int i =0; i <26; i ++){if(tr[0][i]){
fa[tr[0][i]]=0;//多组特殊的初始化不要忘了!!!
q.push(tr[0][i]);}}while(q.size()){int u = q.front(); q.pop();for(int i =0; i <26; i ++){int v = tr[u][i];if(v){
fa[v]= tr[fa[u]][i];
q.push(v);//这点不要忘了QAQ}else
tr[u][i]= tr[fa[u]][i];}}}voidquery(char s[]){int u =0;for(int i =0; s[i]; i ++){
u = tr[u][s[i]-'a'];int v = u;while(v){if(i - last[v]>= dep[v]){
last[v]= i;
ans[v]++;}
v = fa[v];}}}intmain(){int T;scanf("%d",&T);while(T --){init();scanf("%s", s);scanf("%d",&n);for(int i =1; i <= n; i ++){scanf("%s", t);insert(t, i);}build();query(s);for(int i =1; i <= n; i ++){printf("%d\n", ans[id[i]]);}}return0;}