题目大意
- 你需要支持下面几种操作
- 在字符串尾部插入一个字符
- 在字符串尾部删除一个字符
- 将字符串整个输出
- 询问第?次输出的字符串在第?次输出的字符串中出现了多少次
- ?,? ≤ 10^5
分析
- 首先你会发现他打字的方式非常奇妙,实际上不就是在构建一颗Tire吗?P相当于给节点标记,B相当于退回父亲节点,a..z相当于建立新的节点
- 然后跑AC自动机得到fail指针
- 首先得知道如何得出单个操作 x,y。AC自动机则架构在前缀树Tire上,自然地,判断AC自动机上面的两个子串x,y,x是否是y的子串,就等价于x是否是 y某个前缀s 的后缀!在AC自动机上,判断x是s的后缀非常简单,只要看s能否沿着fail指针走到x即可
- 那么,查询x在y中出现了几次就比较明了了,只要看从根节点到y的路径中有多少个s,满足x是s的后缀即s能沿着fail指针走到x
- 于是,我们可以将 fail[i] 作为 i 的父节点建立一颗新的树,这样的话如果 j 是 i 的祖先,那么 i 显然可以由fail指针走到 j。那么查询x,y时,将root->y的路径上的每一个点都变为1,那么答案就相当于x的子树中有多少个1了
- 但是这样直接在线查询显然不行
除非写一些神犇数据结构。单点修改子树查询可以用 dfs序+树状数组解决,因此我们可以离线,以y为关键字排序,这样就可以根据原来建立AC自动机的顺序进行修改了,只要在进入一个点t时+1,出去时-1,查询时自然root->y的路径上的每一个点都是1了
Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int N=1e5+100;
struct node{int y,n;}e[N];
struct ques{int x,y,id;}q[N];
int tr[N][30],ans[N],fa[N],pos[N],fail[N],c[N],l[N],r[N],lin[N],len=1,dfn=0,tot=0,cnt=0,n;
string s;
inline bool cmp(ques x,ques y){return x.y<y.y;}
void read(int x,int y)
{e[++len].y=y,e[len].n=lin[x],lin[x]=len;}
void dfs(int x){
l[x]=++dfn;
for(int i=lin[x];i;i=e[i].n)dfs(e[i].y);
r[x]=dfn;
}
inline int lowbit(int x){return x&-x;}
void add(int x,int v){while(x<=dfn)c[x]+=v,x+=lowbit(x);}
int getsum(int x){int sum=0;while(x)sum+=c[x],x-=lowbit(x);return sum;}
void build(){
int now=0,len=s.size();
for(int i=0;i<len;i++){
if(s[i]=='P') pos[++cnt]=now;
else if(s[i]=='B') now=fa[now];
else{
int &pos=tr[now][s[i]-'a'];
if(!pos)pos=++tot;
fa[pos]=now,now=pos;
}
}
queue<int> q;
rep(i,0,25)if(tr[0][i])q.push(tr[0][i]);
while(q.size()){
int x=q.front();q.pop();
rep(i,0,25){
int &pos=tr[x][i];
if(pos){
fail[pos]=tr[fail[x]][i];
q.push(pos);
}else pos=tr[fail[x]][i];
}
}
}
void work(){
int now=0,k=1,len=s.size();cnt=0;
for(int i=0;i<len;i++){
if(s[i]=='P'){
cnt++;
while(q[k].y==cnt&&k<=n){
int tmp=pos[q[k].x];
ans[q[k].id]=getsum(r[tmp])-getsum(l[tmp]-1);
k++;
}
}else if(s[i]=='B'){
add(l[now],-1);
now=fa[now];
}else{
now=tr[now][s[i]-'a'];
add(l[now],1);
}
}
}
int main()
{
cin>>s>>n;
rep(i,1,n){scanf("%d%d",&q[i].x,&q[i].y);q[i].id=i;}
sort(q+1,q+n+1,cmp);
build(); rep(i,1,tot)read(fail[i],i);
dfs(0);
work(); rep(i,1,n)printf("%d\n",ans[i]);
return 0;
}
鸣谢