【BZOJ2434】【NOI2011】—阿狸的打字机(AC自动机+线段树)

传送门


考虑一个串 A A A在另一个串 B B B中出现的次数

其实就是 B B B串的链上有多少个点的 f a i l fail fail直接或间接指向 A A A的末指针

所以建出 f a i l fail fail树后就是 A A A的末指针的子树和
每次暴力把 B B B的所有点设成1查询可以有 70 70 70

离线后对于每个 B B B记一下要询问哪些就行了
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define gc getchar
inline int read(){
    int res=0,f=1;
    char ch=gc();
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
const int N=100005;
char op[N];
int n,m;
namespace Ac{
    struct node{
        int nxt[27],fail,fa;
        vector<int> e;
        vector<int> id;
    }t[N];
    struct ask{
        int x,y,id;
        friend inline bool operator <(const ask &a,const ask &b){
            return a.y<b.y;
        }
    }q[N];
    vector<int> e[N];
    int in[N],out[N],dfn,tot,ql[N],qr[N],ed[N],ans[N];
    int tr[N];
    #define lb(x) (x&(-x))
    inline void update(int p,int k){
        for(;p<=dfn;p+=lb(p))tr[p]+=k;
    }
    inline int query(int p,int res=0){
        for(;p;p-=lb(p))res+=tr[p];return res;
    }
    inline void buildfail(){
        queue<int> q;
        for(int i=0;i<26;i++)
        if(t[0].nxt[i])q.push(t[0].nxt[i]);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0;i<26;i++){
                int v=t[u].nxt[i];
                if(v)t[v].fail=t[t[u].fail].nxt[i],q.push(v);
                else t[u].nxt[i]=t[t[u].fail].nxt[i];
            }
        }
    }
    void dfs1(int u){
        in[u]=++dfn;
        for(int i=0;i<e[u].size();i++){
            dfs1(e[u][i]);
        }
        out[u]=dfn;
    }
    void dfs2(int u){
        update(in[u],1);
        if(t[u].id.size()){
            for(int j=0;j<t[u].id.size();j++){
                int p=t[u].id[j];
                for(int i=ql[p];i<=qr[p];i++){
                    ans[q[i].id]=query(out[ed[q[i].x]])-query(in[ed[q[i].x]]-1);
                }
            }
        }
        for(int i=0;i<t[u].e.size();i++){
            int v=t[u].e[i];
            dfs2(v);
        }
        update(in[u],-1);
    }
}
using namespace Ac;
int main(){
    scanf("%s",op+1);
    int now=0;
    for(int i=1,len=strlen(op+1);i<=len;i++){
        if(op[i]>='a'&&op[i]<='z'){
            if(!t[now].nxt[op[i]-'a'])t[now].nxt[op[i]-'a']=++tot,t[tot].fa=now;
            now=t[now].nxt[op[i]-'a'];
        }
        if(op[i]=='B')now=t[now].fa;
        if(op[i]=='P')ed[++n]=now,t[now].id.pb(n);
    }
    for(int i=0;i<=tot;i++)
        for(int j=0;j<26;j++)if(t[i].nxt[j])t[i].e.pb(t[i].nxt[j]);
    buildfail();
    for(int i=1;i<=tot;i++)e[t[i].fail].pb(i);
    dfs1(0);
    m=read();
    for(int i=1;i<=m;i++)
        q[i].x=read(),q[i].y=read(),q[i].id=i;
    sort(q+1,q+m+1);
    for(int i=1,pos=1;i<=m;i=pos){
        ql[q[i].y]=i;
        while(q[pos].y==q[i].y)pos++;
        qr[q[i].y]=pos-1;
    }
    dfs2(0);
    for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值