[NOI2011] 阿狸的打字机
题意:
有一个字符序列,初始为空。输入一个长度为m的操作序列,有以下三种操作:
- 输入一个小写字母x,代表在序列末尾添加一个x。
- 输入'B',代表删除序列末尾的一个字母。
- 输入'P',代表输出此时的序列。(不影响序列的状态)
设操作序列中共有n个'P',我们将输出的序列从1-n编号。
你需要回答q个询问,每个询问形如$(x,y)$,代表询问第x个输出序列在第y个输出序列中出现了多少次。
$n,m,q\leq 10^5$。
题解:
挺水的一道题,写了一发过了,惊了。
首先考虑如何求字符串a在字符串b中出现了多少次,显然直接跑KMP即可。
更进一步地,如果要同时求很多个a的答案,显然可以把所有a拿出来建个AC自动机,然后在自动机上跑b。
设当前跑到点u,那么从点u一直往上跳nxt,每跳到一个end节点则该节点对应的a答案+1。
回到本题,我们只需要把所有序列建成一个AC自动机,然后离线枚举每个b跑答案即可。
考虑到暴力建AC自动机是$O(m^{2})$的,我们只需要在每一个操作之后动态更新AC自动机即可,显然每次最多改动一个节点。
考虑到对每个b暴力跳nxt也是$O(m^{2})$的,我们只需要把fail树单独拿出来,显然就是个链+1和单点查询的问题。
可以用树剖解决,也可以把区间加/单点查转化成单点加/区间查,用树状数组维护即可。
复杂度$O(m\log{m})$。
套路:
- 区间加/单点查$\leftrightarrow$单点加/区间查。
代码:
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int C[maxn],ep[maxn]; char str[maxn]; vector<int> vc[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } struct Ques{int x,y,ans;}Q[maxn]; struct Tree{ int nxt[maxn],to[maxn],hd[maxn],id[maxn],rk[maxn],siz[maxn],cnt; inline void addedge(int u,int v){to[++cnt]=v,nxt[cnt]=hd[u],hd[u]=cnt;} inline void dfs(int u){ id[++id[0]]=u,rk[u]=id[0],siz[u]=1; for(int i=hd[u];i;i=nxt[i]) dfs(to[i]),siz[u]+=siz[to[i]]; } }tr; struct ACauto{ int to[maxn][30],nxt[maxn],fa[maxn]; inline void bulid(int n){ for(int i=0;i<26;i++) to[0][i]=1; nxt[1]=0; int now=1,tot=0; for(int i=1;i<=n;i++){ char ch=str[i]; if(ch=='B') now=fa[now]; else if(ch=='P') ep[++tot]=now; else to[now][ch-'a']=i+1,fa[i+1]=now,now=i+1; } queue<int> q; q.push(1); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(to[u][i]) nxt[to[u][i]]=to[nxt[u]][i],q.push(to[u][i]); else to[u][i]=to[nxt[u]][i]; } } for(int i=2;i<=n+1;i++) tr.addedge(nxt[i],i); tr.dfs(1); } }AC; inline int lowbit(int x){return x&(-x);} inline void add(int x,int y,int n){for(int i=x;i<=n;i+=lowbit(i))C[i]+=y;} inline int qry(int x){int r=0;for(int i=x;i;i-=lowbit(i))r+=C[i];return r;} int main(){ scanf("%s",str+1); int n=strlen(str+1),T=read(); AC.bulid(n); for(int i=1;i<=T;i++) Q[i].x=read(),Q[i].y=read(),vc[Q[i].y].push_back(i); int now=1,tot=0; for(int i=1;i<=n;i++){ char ch=str[i]; if(ch=='B') add(tr.rk[now],-1,n+1),now=AC.fa[now]; else if(ch=='P'){ tot++; for(int j=0;j<vc[tot].size();j++){ int p=vc[tot][j],x=Q[p].x,nd=ep[x]; Q[p].ans=qry(tr.rk[nd]+tr.siz[nd]-1)-qry(tr.rk[nd]-1); } } else now=AC.to[now][ch-'a'],add(tr.rk[now],1,n+1); } for(int i=1;i<=T;i++) printf("%d\n",Q[i].ans); return 0; }