【题解】NOI2011阿狸的打字机

【Problem】

bzoj2434
洛谷2414
codevs1946

【Solution】

本着刷AC自动机去做的这题,而且这题一看就是要用数据结构的

这题大致题意是求A在B中出现的次数,联想AC自动机

如果每次保存一下打印出来的字符串,数组君会受不了的

首先考虑优化空间,构造 AC A C 自动机时只保存每个串的结束位置,空间优化到了线性

再考虑时间,利用 fail f a i l 数组,对于询问 A A B中出现多少次,答案为在 A A 中所有节点中有多少通过fail指针直接或间接可到 B B 的结尾节点

但如果每次暴力计算,时间会炸,所以考慮fail树,每次相当于求在以 B B 的结尾节点为根的子树下有多少个A节点,用上树状数组差分维护即可,具体见代码

【Code】
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define rg register
#define cl(x) memset(x,0,sizeof(x))

template <typename _Tp> inline void read(_Tp&x){
    rg char c11=getchar(),ob=0;x=0;
    while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return ;
}

const int N=105000;
struct Query{
    int x,y,id;
    bool operator < (const Query tmp) const {return y<tmp.y;}
}qy[N];
char s[N];int head[N],l[N],r[N],c[N],ans[N],d_=0,_=0,n,m;
struct Edge{int v,nxt;}e[N];

inline void add(int,int);

inline void A_log(int x,int delta){for(;x<=d_;x+=lowbit(x))c[x]+=delta;return ;}

inline int Q_log(int x){rg int tmp=0;for(;x;x-=lowbit(x))tmp+=c[x];return tmp;}

queue <int> q;

struct ac_auto{
    int tot,ch[N][26],fa[N],fail[N],pos[N];
    ac_auto(){tot=0;}
    void build(){
        rg int cnt=0,now=0;
        for(rg int i=1;s[i];++i)
            if(s[i]=='P')pos[++cnt]=now;
            else if(s[i]=='B')now=fa[now];
            else
                if(!ch[now][s[i]-'a'])ch[now][s[i]-'a']=++tot,fa[tot]=now,now=tot;
                else now=ch[now][s[i]-'a'];
        for(rg int i=0;i<26;++i)if(ch[0][i])q.push(ch[0][i]);
        while(!q.empty()){
            rg int x=q.front();q.pop();
            for(rg int i=0;i<26;++i)
                if(ch[x][i])
                    q.push(ch[x][i]),fail[ch[x][i]]=ch[fail[x]][i];
                else ch[x][i]=ch[fail[x]][i];
        }
        return ;
    }
    void work(){
        rg int cnt=0,now=0,k=1;
        for(rg int i=1;s[i];++i)
            if(s[i]=='P')
                for(++cnt;qy[k].y==cnt&&k<=m;++k)
                    ans[qy[k].id]=Q_log(r[pos[qy[k].x]])-Q_log(l[pos[qy[k].x]]-1);
            else if(s[i]=='B')
                A_log(l[now],-1),now=fa[now];
            else now=ch[now][s[i]-'a'],A_log(l[now],1);
        return ;
    }
}ac;

inline void dfs(int x){
    l[x]=r[x]=++d_;
    for(rg int i=head[x];i;i=e[i].nxt)
        dfs(e[i].v),r[x]=r[e[i].v];
    return ;
}

int main(){
    scanf("%s",s+1);
    ac.build();
    for(rg int i=1;i<=ac.tot;++i)add(ac.fail[i],i);
    dfs(0);read(m);
    for(rg int i=1;i<=m;++i)read(qy[i].x),read(qy[i].y),qy[i].id=i;
    sort(qy+1,qy+m+1);ac.work();
    for(rg int i=1;i<=m;++i)printf("%d\n",ans[i]);
    return 0;
}

inline void add(int u,int v){e[++_].v=v,e[_].nxt=head[u],head[u]=_;return ;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值