题目链接
emmm,为什么最近天天碰到卡这种那种的题目啊,真的自闭了……
首先吧,这题最暴力的两种做法
建5000*5000的ac自动机
建带线段树合并的SAM
思路都比较简单,而且如果不卡空间的话复杂度都挺对
但可惜它卡空间……
这个时候对应的有两种优化方法
分块定期重构的ac自动机
暴力SAM……
因为SAM比较好想就写了SAM
思路是先对长串建SAM,把每个短串都在SAM里匹配一遍,如果能完全匹配说明这个串能用
在这个串对应的节点上插个长度,接着dfs的时候往下推mx,推到新加进来的np节点,这个时候np节点的mx就是以这个点为结尾最长被覆盖的位置,差分一下就可以了
但其实如果他把后缀自动机卡满也就是开到6e5的话,肯定还是会MLE的……
代码如下:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int len,fa,son[26];
}t[500050];
int head[500050],nxt[500050],to[500050];
int last=1,cnt=1,mx[500050],pos[300010],vis[300010];
int ans,tot;
char s[300010];
void add(int c)
{
int p=last;
int np=++cnt;
t[np].len=t[p].len+1;
while(p&&(!t[p].son[c]))
{
t[p].son[c]=np;
p=t[p].fa;
}
if(!p) t[np].fa=1;
else
{
int q=t[p].son[c],nq;
if(t[p].len+1==t[q].len)
{
t[np].fa=q;
}
else
{
nq=++cnt;
t[nq]=t[q];
t[nq].len=t[p].len+1;
t[np].fa=t[q].fa=nq;
while(p&&t[p].son[c]==q)
{
t[p].son[c]=nq;
p=t[p].fa;
}
}
}
last=np;
}
void dfs(int now)
{
mx[now]=max(mx[t[now].fa],mx[now]);
for(int i=head[now];i;i=nxt[i])
{
dfs(to[i]);
}
}
int n,m;
int main()
{
scanf("%d",&n);
scanf("%s",s);
int lena=strlen(s);
int len=lena;
for(int i=0;i<lena;i++) add(s[i]-'a'),pos[i]=last;
scanf("%d",&m);
for(int ttt=1;ttt<=m;ttt++)
{
scanf("%s",s);
lena=strlen(s);
int rt=1;
for(int i=0;i<lena;i++)
{
if(!t[rt].son[s[i]-'a'])
{
rt=-1;
break;
}
else rt=t[rt].son[s[i]-'a'];
}
if(~rt)
{
mx[rt]=max(mx[rt],lena);
}
}
for(int i=1;i<=cnt;i++)
{
nxt[++tot]=head[t[i].fa];
to[tot]=i;
head[t[i].fa]=tot;
}
dfs(1);
for(int i=0;i<len;i++) vis[i-mx[pos[i]]+1]++,vis[i+1]--;
for(int i=1;i<len;i++) vis[i]=vis[i-1]+vis[i];
for(int i=0;i<len;i++) ans+=(!vis[i]);
printf("%d\n",ans);
}