1.分析
朴素的算法是把所有字符串处理出来后对于每个询问进行一次kmp,然并卵,肯定超时。
因为有很多字符串,我们可以考虑建立一个AC自动机。这样我们又有了一种思路,假如顺着某个节点的fail指针走,最后可以走到某个字符串的末尾,那么表示这个节点所在的字符串里包括一个那个能走到末尾的字符串。不信看图:
可是这样子搞还是太慢了。
我们可以根据反向的fail指针建立一棵fail树,这样的话一个节点子树里的所有节点就是走fail可以到达它的,那么我们就想到了dfs序,用树状数组维护。
不过怎么计算呢?我们可以预先存下所有的询问。
我们开始遍历这棵trie树(遍历就按照打字机顺序即可,我没有是因为懒得改了),每到一个节点,就在树状数组里这个节点dfs序入序的位置+1,每离开一个节点,就-1。
假如这个节点是一个字符串的末尾,那么就处理询问,此时树状数组里存的肯定是关于这整个字符串的信息。处理询问是比如是aaa我们处理的询问是其他字符串在aaa里出现了几次这样的询问,答案就是哪个字符串dfs出序和入序之间的那个区间里的值的和。
2.代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
char s[100005];
int fa[100005],a[100005][27],ne[100005],bh[100005];
int que[100005],ks[100005],ans[100005],sum[100005],mo[100005];//ks:询问的开始处
struct fil{int h,in,lif;}tree[100005];
struct tx{int xia;int go;}edg[100005],qus[100005];
int m,sz=1,tot=0,ti,cnt=0;//tot:边的数量
int read(){
int q=0;char ch=' ';
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){q=q*10+ch-'0',ch=getchar();}
return q;
}
void tri(){
int i,j,t,from=1,las=1,l=strlen(s);
for(i=0;i<l;i++){
if(s[i]=='P'){cnt++;mo[cnt]=from;bh[from]=cnt;}
else if(s[i]=='B'){from=fa[from];}
else {
t=s[i]-'a'+1;
if(a[from][t])from=a[from][t];
else {sz++;fa[sz]=from;a[from][t]=sz;from=sz;}
}
}
}
void add(int x,int y){tot++;edg[tot].go=y;edg[tot].xia=tree[x].h;tree[x].h=tot;}
void ac(){//构建fail树?
int i,j,head=1,tail=1,from;
que[1]=1;
while(head<=tail){
from=que[head];
for(i=1;i<=26;i++){
if(!a[from][i])continue;
j=ne[from];
while(!a[j][i])j=ne[j];
ne[a[from][i]]=a[j][i];add(a[j][i],a[from][i]);
tail++;que[tail]=a[from][i];
}
head++;
}
}
void dfs(int x){//求dfs序
int i;
ti++;tree[x].in=ti;
for(i=tree[x].h;i;i=edg[i].xia)dfs(edg[i].go);
tree[x].lif=ti;
}
int lowbit(int x){return x&(-x);}
int jia(int x){int i;while(x<=sz){sum[x]++;x+=lowbit(x);}}
void jian(int x){int i;while(x<=sz){sum[x]--;x+=lowbit(x);}}
int getans(int x){
int da=0;
while(x>0){da+=sum[x];x-=lowbit(x);}
return da;
}
void goin(int x){
int i,kl;
if(x!=1)jia(tree[x].in);
for(i=1;i<=26;i++)
if(a[x][i])goin(a[x][i]);
if(bh[x]){
for(i=ks[bh[x]];i!=0;i=qus[i].xia){kl=mo[qus[i].go];
ans[i]=getans(tree[kl].lif)-getans(tree[kl].in-1);}
}
if(x!=1)jian(tree[x].in);
}
int main()
{
int i,j,l,x,y;
scanf("%s",s);
for(i=1;i<=26;i++)a[0][i]=1;
tri();ac();dfs(1);
m=read();
for(i=1;i<=m;i++){
x=read();y=read();
qus[i].go=x;qus[i].xia=ks[y];ks[y]=i;
}
goin(1);
for(i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}