[NOI2011] 阿狸的打字机

题意:

有一个字符序列,初始为空。输入一个长度为m的操作序列,有以下三种操作:

  1. 输入一个小写字母x,代表在序列末尾添加一个x。
  2. 输入'B',代表删除序列末尾的一个字母。
  3. 输入'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;
}
阿狸的打字机

 

posted @ 2020-07-20 10:25  Fugtemypt  阅读(139)  评论(0编辑  收藏  举报