题意:
给出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; }