好难啊,这个题需要很强的技巧和码力
首先ac自动机不难想到,暴力跳fail也不难想到,但建fail树没想到
建好之后问题就变成了随着点的加入,求一个子树里1的个数
然后由于询问一定在一个子树里,而加点的时候合法的一定在内部(dfs序内部),所以差分即可求一段中1的和,可用树状数组加速
码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define N 200010
int sz=1,T=1,len,t[N],i,x,y,l[N],r[N],ch[N][27],ans[N],ci[N],fu[N],sp[N],m;
char str[N];
queue<int>q;
vector<int>v[100005],wen[100005],id[100005];
int lowbit(int a)
{
return a&(-a);
}
void add(int x,int y)
{ int i;
for(i=x;i<=T;i+=lowbit(i))
{
t[i]+=y;
}
}
int qiu(int x)
{
int ans=0,i;
for(i=x;i>0;i-=lowbit(i))
{
ans+=t[i];
}
return ans;
}
void jia()
{
int i,o=1,lin=0;
for(i=0;i<len;i++)
if(str[i]=='P')
ci[++lin]=o;else
if(str[i]=='B')
o=fu[o];else
{
if(ch[o][str[i]-'a'+1]==0)ch[o][str[i]-'a'+1]=++sz,fu[sz]=o;
o=ch[o][str[i]-'a'+1];
}
}
void shipei()
{
int i;
q.push(1);
while(!q.empty())
{
int st=q.front();
q.pop();
for(i=1;i<=26;i++)
{
if(ch[st][i]==0)continue;
int k=sp[st];
while(ch[k][i]==0)k=sp[k];
sp[ch[st][i]]=ch[k][i];
q.push(ch[st][i]);
}
}
}
void dfs(int o)
{
l[o]=++T;
for(int i=0;i<v[o].size();i++)
dfs(v[o][i]);
r[o]=++T;
}
void work()
{
int o=1,dc=0;
add(l[1],1);
for(int j=0;j<len;j++)
if(str[j]=='P')
{
++dc;
for(i=0;i<wen[dc].size();i++)
{
int x=ci[wen[dc][i]];
ans[id[dc][i]]=qiu(r[x])-qiu(l[x]-1);
}
}else if(str[j]=='B')add(l[o],-1),o=fu[o];
else{
o=ch[o][str[j]-'a'+1];
add(l[o],1);
}
}
int main()
{
for(i=1;i<=26;i++)
ch[0][i]=1;
scanf("%s",str); len=strlen(str);
jia();shipei();
for(i=1;i<=sz;i++)
v[sp[i]].push_back(i);
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
wen[y].push_back(x);
id[y].push_back(i);
}
dfs(1);
work();
for(i=1;i<=m;i++)
printf("%d\n",ans[i]);
}