题目大意
给定n个字符串,q个询问,每个询问给定一个字符串,求它在n个字符串中多少个中以连续子串形式出现。
n<=10000, q<=60000
the total length of n strings<=100000,
the total length of q question strings<=360000
分析
首先很容易想到后缀自动机。
给n个字符串建广义后缀自动机,然后每个询问串就在自动机上跑到相应节点,然后在fail树子树上找有多少个不同的字符串。
那么可以离线读入询问,然后按照dfs序枚举,保留每个字符串最后出现的位置,查询操作就是一个数组内有多少个大于x的值。这个用树状数组维护即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int N=200005;
typedef long long LL;
int n,q,tot,last,ans[N],fail[N],step[N],st[N],m,en[N],D[N],dfn[N],h[N],E[N],nxt[N],fi[N],T[N],now[N];
map <int,int> e[N];
vector <int> Q[N],H[N];
char s[N<<1],t[N];
void add(char c)
{
int p=last,np,q,nq;
if (e[p][c]>0 && step[e[p][c]]==step[p]+1)
{
last=e[p][c];
return;
}
for (step[last=np=++m]=step[p]+1;p>=0 && !e[p][c];p=fail[p]) e[p][c]=np;
if (p<0) fail[np]=0;else
{
q=e[p][c];
if (step[q]==step[p]+1) fail[np]=q;else
{
nq=++m;
e[nq]=e[q];
fail[nq]=fail[q];
step[nq]=step[p]+1;
fail[q]=fail[np]=nq;
for (;p>=0 && e[p][c]==q;p=fail[p]) e[p][c]=nq;
}
}
}
void link(int x,int y)
{
E[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}
void dfs(int x)
{
dfn[x]=++tot;
for (int i=h[x];i;i=nxt[i]) dfs(E[i]);
en[x]=tot;
}
int lowbit(int x)
{
return x&-x;
}
void change(int x,int y)
{
for (;x<=tot;x+=lowbit(x)) T[x]+=y;
}
int sum(int x)
{
int k=0;
for (;x;x-=lowbit(x)) k+=T[x];
return k;
}
int main()
{
scanf("%d%d",&n,&q);
fail[0]=-1;
for (int i=0;i<n;i++)
{
scanf("%s",t);
st[i+1]=st[i]+strlen(t);
last=0;
for (int j=0;j<st[i+1]-st[i];j++)
{
s[j+st[i]]=t[j];
add(t[j]);
}
}
for (int i=1;i<=m;i++) link(fail[i],i);
tot=0;
dfs(0);
for (int i=0;i<n;i++)
{
for (int j=st[i],k=0;j<st[i+1];j++)
{
k=e[k][s[j]];
H[dfn[k]].push_back(i);
}
}
for (int i=0;i<q;i++)
{
scanf("%s",t);
bool bz=1;
int x=0;
for (int j=0,k=strlen(t);j<k;j++)
{
if (!e[x][t[j]])
{
bz=0; break;
}
x=e[x][t[j]];
}
if (bz)
{
fi[i]=dfn[x]-1; Q[en[x]].push_back(i);
}
}
for (int i=1;i<=tot;i++)
{
vector <int> ::iterator it;
for (it=H[i].begin();it!=H[i].end();it++)
{
if (now[*it]>0) change(now[*it],-1);
now[*it]=i;
change(now[*it],1);
}
for (it=Q[i].begin();it!=Q[i].end();it++)
{
ans[*it]=sum(tot)-sum(fi[*it]);
}
}
for (int i=0;i<q;i++) printf("%d\n",ans[i]);
return 0;
}