省队集训=自闭+水题+颓废
卡空间的$AC$自动机板子题。不会什么奇技淫巧于是选择了线性空间的$SAM$。
显然要对母串造$SAM$,把模式串在$SAM$跑一遍。如果一个模式串在节点$i$停下,它就能和所有属于$endpos_i$的位置匹配。
维护一个$ma_i$表示在节点$i$停下的最长的模式串长度。每个节点给区间$[\max\{pos-ma_i+1,1\},pos] (pos\in endpos_i)$加$1$。某个位置大于$0$说明可以修补,差分即可。
当然我们并不需要对每个节点都操作。一个点的$endpos$都来自于$parent\ tree$的子树,把$ma$推下去在叶子结点操作即可。
一开始开的$map$,不过额外空间开销不小还是$M$了,于是厚颜无耻地开数组靠动态内存$A$了。要没有动态内存的话。。。也许要手写平衡树?
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define maxn 600005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
#define son(x,y) son[x][y]
int son[maxn][26],fa[maxn],len[maxn],ma[maxn],sum[maxn],pos[maxn],cur[maxn],tax[maxn],all,num,last=1,cnt=1;
char s[maxn];
inline void modify(int l,int r){
++sum[l],--sum[r+1];
}
void insert(int c){
int p=last,ne=last=++cnt;
pos[ne]=len[ne]=len[p]+1;
while(p&&!son(p,c))son(p,c)=ne,p=fa[p];
if(!p)fa[ne]=1;
else {
int q=son(p,c);
if(len[q]==len[p]+1)fa[ne]=q;
else {
int sp=++cnt;
memcpy(son[sp],son[q],sizeof son[q]);
len[sp]=len[p]+1,fa[sp]=fa[q],fa[q]=fa[ne]=sp;
while(p&&son(p,c)==q)son(p,c)=sp,p=fa[p];
}
}
}
int main(){
int n=read(),m,t,node,ans=0;
scanf("%s",s+1);
for(register int i=1;i<=n;++i)insert(s[i]-'a');
for(register int i=1;i<=cnt;++i)++tax[len[i]];
for(register int i=1;i<=n;++i)tax[i]+=tax[i-1];
for(register int i=1;i<=cnt;++i)cur[tax[len[i]]--]=i;
t=read();
while(t--){
scanf("%s",s+1),m=strlen(s+1),node=1;
for(register int i=1;i<=m&&node;++i)node=son(node,s[i]-'a');
if(node)ma[node]=max(ma[node],m);
}
for(register int i=2;i<=cnt;++i){
t=cur[i];
ma[t]=max(ma[t],ma[fa[t]]);
if(ma[t]&&pos[t])modify(max(pos[t]-ma[t]+1,1),pos[t]);
}
for(register int i=1;i<=n;++i){
sum[i]+=sum[i-1];
if(!sum[i])++ans;
}
printf("%d\n",ans);
}