bzoj 2434 阿狸的打字机 AC自动机+fail树

询问x串在y串中出现次数,就是有多少y串的节点能顺着fail边跑到x的终止节点。所以建出fail树,求end[x] 为根的子树中有多少y的节点。
这个可以离线询问,用树状数组维护。

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 200005
using namespace std;
void read(int &a)
{
    a=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')
    {
        a*=10;a+=c-'0';
        c=getchar();
    }
}
int c[maxn];
int N;
int low(int x)
{return (x&(-x));}
void add(int x,int d)
{
    while(x<=N)
    {
        c[x]+=d;
        x+=low(x);
    }
}
int ask(int x)
{
    int ans=0;
    while(x>=1)
    {
        ans+=c[x];
        x-=low(x);
    }
    return ans;
}
struct QUE{int x,y,id;}q[maxn];
bool cmp(QUE A,QUE B)
{return A.y<B.y;}
int ANS[maxn];
char s[maxn];
int T,fa[maxn];
struct E{int to,nxt;}b[maxn<<1];
int id[maxn],fst[maxn],tot;
void ins(int f,int t)
{
    b[++tot]=(E){t,fst[f]};fst[f]=tot;
    b[++tot]=(E){f,fst[t]};fst[t]=tot;
}
int dfn[maxn],dfs_c;
int in[maxn],out[maxn];
void dfs(int x)
{
    dfn[++dfs_c]=x;
    in[x]=dfs_c;
    for(int i=fst[x];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!in[v]) dfs(v);
    }
    out[x]=dfs_c;
}
struct Trie
{
    int val[maxn];
    int ch[maxn][30],cnt;
    int lst[maxn],fail[maxn];
    void insert()
    {
        int p=0;
        int n=strlen(s+1);
        for(int i=1;i<=n;i++)
        {
            if(s[i]>='a'&&s[i]<='z')
            {
                int c=s[i]-'a'+1;
                if(!ch[p][c])
                {
                    ch[p][c]=++cnt;
                    fa[ch[p][c]]=p;
                }
                p=ch[p][c];
            }
            else if(s[i]=='B')p=fa[p];
            else id[++T]=p;
        }
    }
    queue<int> Q;
    void build()
    {
        for(int i=1;i<=26;i++)
            if(ch[0][i])
            {
                Q.push(ch[0][i]);
                ins(0,ch[0][i]);
            }
        while(!Q.empty())
        {
            int r=Q.front();Q.pop();
            for(int i=1;i<=26;i++)
            {
                int u=ch[r][i];
                if(!u)
                {
                    ch[r][i]=ch[fail[r]][i];
                    continue;
                }
                Q.push(u);
                int p=fail[r];
                while(p&&!ch[p][i]) p=fail[p];
                fail[u]=ch[p][i];
                ins(ch[p][i],u);
            }
        }
    }
    void work()
    {
        int p=0,H=1,t=0;
        int n=strlen(s+1);
        for(int i=1;i<=n;i++)
        {
            if(s[i]>='a'&&s[i]<='z')
            {
                p=ch[p][s[i]-'a'+1];
                add(in[p],1);
            }
            else if(s[i]=='B')
            {
                add(in[p],-1);
                p=fa[p];
            }
            else
            {
                t++;
                while(q[H].y==t)
                {
                    int x=id[q[H].x];
                    ANS[q[H].id]=ask(out[x])-ask(in[x]-1);
                    H++;
                }
            }
        }
    }
}AC;
int main()
{
    scanf("%s",s+1);
    AC.insert();AC.build();
    int m;scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        read(q[i].x);
        read(q[i].y);
        q[i].id=i;
    }
    dfs(0);N=dfs_c;
    sort(q+1,q+m+1,cmp);
    AC.work();
    for(int i=1;i<=m;i++)
        printf("%d\n",ANS[i]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值