题目
A镇的主街是由N个小写字母构成(S串),镇长准备在上面贴瓷砖,瓷砖一共有M种,第i种上面有Li个小写字母,瓷砖不能旋转也不能被分割开来,瓷砖只能贴在跟它身上的字母完全一样的地方,允许瓷砖重叠,并且同一种瓷砖的数量是无穷的。
问街道有多少字母(地方)不能被瓷砖覆盖。
题解
有卡住人的地方。
①匹配的时候还没走到底端但已经确认能够匹配了。
②建造AC自动机是以谁来建造,怎么跑?
很容易想到的是,按照瓷砖来构建AC自动机。然后用S串跑一遍。
这个怎么办?
设up[i]表示i的fail链上第一个“终点”。
然后构造fail链的时候同时处理up[]。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define N 4000010
#define M 300010
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,x,y,z,tot,ans;
int fail[N],up[N],c[N],tr[N][26],dep[N];
int sum[M];
char s[N],s1[5010],ch;
queue<int>qu;
int main(){
scanf("%d\n",&n);
fo(i,1,n)s[i]=getchar();
scanf("%d\n",&m);
dep[0]=0;
while(m--){
l=0;
ch=getchar();
while(ch<'a'||ch>'z')ch=getchar();
while(ch>='a'&&ch<='z')s1[++l]=ch,ch=getchar();
k=0;
fo(i,1,l){
x=s1[i]-'a';
if(tr[k][x])k=tr[k][x];
else{
tr[k][x]=++tot;
dep[tot]=dep[k]+1;
k=tot;
}
if(i==l)c[k]=1;
}
}
fail[0]=-1;
qu.push(0);
while(!qu.empty()){
x=qu.front();
qu.pop();
fo(i,0,25){
if(!tr[x][i])continue;
y=tr[x][i];z=fail[x];
while(~z&&!tr[z][i])z=fail[z];
fail[y]=z==-1?0:tr[z][i];
qu.push(y);
up[y]=y;
if(!c[y])up[y]=up[fail[y]];
}
}
j=0;
fo(i,1,n){
x=s[i]-'a';
while(~j&&!tr[j][x])j=fail[j];
j=j==-1?0:tr[j][x];
sum[i-dep[up[j]]+1]++;
sum[i+1]--;
}
k=0;ans=n;
fo(i,1,n){k+=sum[i];if(k)ans--;}
printf("%d",ans);
return 0;
}