【题解】
KMP算法:每次询问时求出x串的失配函数,然后在y串上匹配,总复杂度O(m*len) 可以得40分
如果把所有单词建成一棵字母树,考虑类似的暴力:
对每次询问,枚举y串的每个点(将这个点理解为x在y串上的最后一个匹配点),若从它沿失配指针到root的路径经过x串的最后一个点,则答案加1
由于(x1,y),(x2,y),…这些y相同的询问在AC自动机上走的路径一样,可以对于y一次全部求得,将询问统计后离线处理即可
做到这一步后,程序的时间主要浪费在哪里呢?发现对于每个y串,都要处理它的每个字符到root的路径,而其实许多串都有很长的公共前缀,这里重复计算了
可以反过来考虑:每个x串的最后一个点,沿失配指针逆向走,寻找所有y上的点
这时如果把x看成一棵树的树根,把失配指针看成树边,那问题就转化为求以x为根的子树中有多少点在y串上
因此,以失配指针为树边,将AC自动机建成一棵树(称fail树),只需要想办法把y上的所有点标在树上,并可以实现快速查询就行了
这样的话,若y2与y1有公共前缀,标记y2的点时公共前缀就不用重新操作了
快速查询子树中有多少标记点,可以联想到区间快速求和(树状数组)
而子树的确可以转化为一段连续的区间:
dfs整棵fail树,对每个结点记录进入、最后离开时间(L,R),则满足L<=L1<R1<=R的点(L1,R1)必在点(L,R)的子树中
因此y串每标记一个点t,就是将fail树对应数列的L[t]位+1,删除同理,询问时查询( L[x_last] , R[x_last] )的区间和就行了
【代码】
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<vector>
using namespace std;
vector<int> Ask[100005],Ans[100005],G[100005];//Ask[i]:所有y==i的询问对应的x
char s[100005]={0};
int ch[100005][130]={0},ls[100005]={0},pre[100005]={0},L[100005]={0},R[100005]={0},c[200005]={0},f[100005]={0},q[100005]={0},x[100005]={0},y[100005]={0},p[100005]={0};
int len,u=0,sz=0,tot=0;
void tj(char t)
{
if(ch[u][t]==0)
{
ch[u][t]=++sz;
pre[ch[u][t]]=u;
}
u=ch[u][t];
}
void getf()
{
int i,j,head=0,tail=0;
for(i='a';i<='z';i++)
if(ch[0][i]!=0)
{
q[tail++]=ch[0][i];
G[0].push_back(ch[0][i]);
}
while(head<tail)
{
for(i='a';i<='z';i++)
if(ch[q[head]][i]!=0)
{
q[tail++]=ch[q[head]][i];
j=f[q[head]];
while(j!=0&&ch[j][i]==0) j=f[j];
f[ch[q[head]][i]]=ch[j][i];
G[ch[j][i]].push_back(ch[q[head]][i]);
}
head++;
}
}
void dfs(int x)
{
int i;
L[x]=++tot;
for(i=0;i<G[x].size();i++)
dfs(G[x][i]);
R[x]=++tot;
}
void jia(int p,int x)//第p个位置的值加x
{
for(;p<=2*len;p+=(p&(-p)))
c[p]+=x;
}
int cx(int p)
{
int sum=0;
for(;p>0;p-=(p&(-p)))
sum+=c[p];
return sum;
}
int main()
{
int m,i,j=0,k;
scanf("%s",s);
len=strlen(s);
for(i=0;i<len;i++)
{
if(s[i]>='a'&&s[i]<='z') tj(s[i]);
if(s[i]=='B') u=pre[u];
if(s[i]=='P') ls[++j]=u;
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
Ask[y[i]].push_back(x[i]);
}
getf();
dfs(0);
u=k=0;
for(i=0;i<len;i++)
{
if(s[i]>='a'&&s[i]<='z')
{
u=ch[u][s[i]];
jia(L[u],1);
}
if(s[i]=='B')
{
jia(L[u],-1);
u=pre[u];
}
if(s[i]=='P')
{
k++;
for(j=0;j<Ask[k].size();j++)
Ans[k].push_back(cx(R[ ls[Ask[k][j]] ])-cx(L[ ls[Ask[k][j]] ]-1));
}
}
for(i=1;i<=m;i++)
printf("%d\n",Ans[y[i]][p[y[i]]++]);
return 0;
}