题意:
给出n个字符串和m个询问串;
求每个询问串是多少个字符串的子串;
n<=10000,m<=60000;
字符串总长度<=100000,询问串总长度<=360000;
题解:
传说中的多串匹配用的广义后缀自动机;
构建上的不同只在当trans(last,x)这个状态存在的时候,要进行一个讨论;
(当然,在单串的自动机中last不会有任何trans转移,所以这种情况只会在广义后缀自动机中出现;
具体就是如果last和trans(last,x)的len只差个1,那么trans(last,x)就是我们这次要插入的结点了,直接返回;
不然,就像下面的一样复制一个trans(last,x)结点搞一搞就可以了;
然后为了区分所有字符串 的后缀,我们在后缀结点上挂链标记就可以了;
对于一个输入的询问串,这个问题就等价于在广义后缀树上找到能代表这个串的结点,然后查询这个结点的子树内不同颜色数;
这个东西很像那个HH的项链嘛,搞出来个DFS序,然后预处理所有结点的答案即可;
因为这个问题最多有O(n)种询问,也就是说可以预处理出来之后再在线回答咯;
代码:
#include<map>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define M 370000
#define K 70000
using namespace std;
char str[M];
int next[N<<1],to[N<<1],head[N<<1],pre[N],ce,tim;
int sum[N<<1],ans[N<<1];
namespace col
{
int next[N<<1],val[N<<1],head[N<<1],ce;
int add(int x,int y)
{
val[++ce]=y;
next[ce]=head[x];
head[x]=ce;
}
}
int add(int x,int y)
{
to[++ce]=y;
next[ce]=head[x];
head[x]=ce;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)
{
if(!x) return ;
while(x<N<<1)
{
sum[x]+=val;
x+=lowbit(x);
}
}
int query(int x)
{
int ret=0;
while(x)
{
ret+=sum[x];
x-=lowbit(x);
}
return ret;
}
void dfs(int x)
{
int t=++tim;
for(int i=col::head[x];i;i=col::next[i])
{
update(pre[col::val[i]],-1);
update(t,1);
pre[col::val[i]]=t;
}
for(int i=head[x];i;i=next[i])
{
dfs(to[i]);
}
ans[x]=query(tim)-query(t-1);
}
namespace SAM
{
int len[N<<1],pre[N<<1];
map<char,int>son[N<<1];
int tot,last;
int newnode()
{
tot++;
// len[tot]=pre[tot]=0;
// son[tot].clear();
return tot;
}
void init()
{
tot=0;
last=newnode();
}
void Insert(char x,int i)
{
if(son[last][x])
{
int p=son[last][x];
if(len[p]==len[last]+1)
last=p;
else
{
int np=newnode();
len[np]=len[last]+1;
pre[np]=pre[p];
pre[p]=np;
son[np]=son[p];
for(int q=last;son[q][x]==p;q=pre[q])
son[q][x]=np;
last=np;
}
}
else
{
int p,np=newnode();
len[np]=len[last]+1;
for(p=last;p&&!son[p][x];p=pre[p])
son[p][x]=np;
if(!p)
pre[np]=1;
else
{
int q=son[p][x];
if(len[q]==len[p]+1)
pre[np]=q;
else
{
int nq=newnode();
len[nq]=len[p]+1;
pre[nq]=pre[q];
son[nq]=son[q];
pre[q]=pre[np]=nq;
for(;son[p][x]==q;p=pre[p])
son[p][x]=nq;
}
}
last=np;
}
col::add(last,i);
}
void Build()
{
for(int i=1;i<=tot;i++)
add(pre[i],i);
}
int query(char *s)
{
int p=1;
while(*s!='\0')
p=son[p][*s],s++;
return p;
}
}
int main()
{
int n,m,len,i,j,k;
scanf("%d%d",&n,&m);
SAM::init();
for(i=1;i<=n;i++)
{
scanf("%s",str+1);
len=strlen(str+1);
SAM::last=1;
for(j=1;j<=len;j++)
SAM::Insert(str[j],i);
}
SAM::Build();
dfs(1);
for(i=1;i<=m;i++)
{
scanf("%s",str);
printf("%d\n",ans[SAM::query(str)]);
}
return 0;
}