• [NOI2011] 阿狸的打字机


    题意:

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

    1. 输入一个小写字母x,代表在序列末尾添加一个x。
    2. 输入'B',代表删除序列末尾的一个字母。
    3. 输入'P',代表输出此时的序列。(不影响序列的状态)

    设操作序列中共有n个'P',我们将输出的序列从1-n编号。

    你需要回答q个询问,每个询问形如$(x,y)$,代表询问第x个输出序列在第y个输出序列中出现了多少次。

    $n,m,qleq 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(mlog{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
    ",Q[i].ans);
        return 0;
    }
    阿狸的打字机
  • 相关阅读:
    C++_标准模板库STL概念介绍2-泛型编程
    C++_标准模板库STL概念介绍1-建立感性认知
    C++_新特性1-类型转换运算符
    C++_新特性2-RTTI运行阶段类型识别
    C++_异常9-异常的注意事项
    C++_异常8-异常、类和基础
    C++_异常7-exception类
    C++_异常6-其他异常特性
    redis数据类型之—List
    redis数据类型之—Hash
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13343674.html
Copyright © 2020-2023  润新知