bzoj2434: [Noi2011]阿狸的打字机(ac自动机+dfs序+bit)

传送门
题意:
有一个打字机,有三种操作:

  1. 打一个小写字母
  2. 删去最近打的一个小写字母
  3. 将打印当前的字符串

现在多组询问,每次问第 x x x个被打印串在第 y y y个被打印串中出现次数。
思路:
按照题意先建出 t r i e trie trie树与 f a i l fail fail树。
然后考虑第 x x x个打印串在第 y y y个被打印串中出现次数等于 f a i l fail fail树上面 x x x子树中属于字符串 y y y的节点数。
于是我们离线排序+ d f s dfs dfs序+ b i t bit bit维护即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
    int ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
typedef pair<int,int> pii;
const int N=2e5+5;
char s[N];
int cnt=0,n,m;
vector<pii>qry[N];
int ans[N];
namespace Bit{
    int bit[N];
    inline int lowbit(const int&x){return x&-x;}
    inline void update(int p,const int&v){for(;p<=cnt;p+=lowbit(p))bit[p]+=v;}
    inline int query(int p){int ret=0;for(;p;p^=lowbit(p))ret+=bit[p];return ret;}
}
namespace acam{
    int son[N][26],rt=1,tot=1,in[N],fail[N],out[N],fa[N],ed[N];
    vector<int>e[N];
    void dfs(int p){
        in[p]=++cnt;
        for(ri i=0;i<e[p].size();++i)dfs(e[p][i]);
        out[p]=cnt;
    }
    inline void getfail(){
        static int q[N],hd,tl;
        hd=1,tl=0;
        for(ri i=0;i<26;++i)if(son[rt][i])q[++tl]=son[rt][i],fail[son[rt][i]]=rt;
        while(hd<=tl){
            int p=q[hd++];
            e[fail[p]].push_back(p);
            for(ri i=0;i<26;++i){
                if(!son[p][i]){son[p][i]=son[fail[p]][i];continue;}
                fail[son[p][i]]=son[fail[p]][i]?son[fail[p]][i]:rt,q[++tl]=son[p][i];
            }
        }
    }
    inline void build(){
        for(ri p=1,i=1,up=strlen(s+1),x;i<=up;++i){
            if(s[i]>='a'&&s[i]<='z'){
                if(!son[p][x=s[i]-'a'])son[p][x]=++tot,fa[tot]=p;
                p=son[p][x];
            }
            if(s[i]=='B')p=fa[p];
            if(s[i]=='P')ed[++n]=p;
        }
        getfail();
        dfs(rt);
    }
    inline int query(int p){return Bit::query(out[ed[p]])-Bit::query(in[ed[p]]-1);}
    inline void solve(){
        for(ri p=1,i=1,nn=0,up=strlen(s+1),x;i<=up;++i){
            if(s[i]>='a'&&s[i]<='z'){
                p=son[p][s[i]-'a'];
                Bit::update(in[p],1);
            }
            if(s[i]=='B')Bit::update(in[p],-1),p=fa[p];
            if(s[i]=='P'){
                ++nn;
                for(ri j=0;j<qry[nn].size();++j)ans[qry[nn][j].se]=query(qry[nn][j].fi);
            }
        }
    }
}
int main(){
    scanf("%s",s+1);
    acam::build();
    m=read();
    for(ri x,y,i=1;i<=m;++i)x=read(),y=read(),qry[y].push_back(pii(x,i));
    acam::solve();
    for(ri i=1;i<=m;++i)cout<<ans[i]<<'\n';
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值