BZOJ 2434: [Noi2011]阿狸的打字机(AC自动机/Fail树 + BIT + DFS序)

题目

题目链接

分析

这么多串弄个AC自动机吧。。可以在O(n)的时间复杂度弄出那个trie来的

然后如果暴力匹配每个y会超时

有一种叫做fail树的东西,就是把fail指针作为边弄成一棵树,根还是没变,这样的话每个结点所代表的字符串(rt->该结点)是它所有子树所代表的字符串的后缀。
对于一个询问(x,y),x可以对它的子树产生影响,只需要统计它的子树中有多少个结点是rt->y即那次询问的答案。

如何统计答案呢?我们去DFS建立的trie树,然后保持DFS序中为1的元素是根节点到当前结点这条链中的元素,然后对于一个y,我们DFS序中所有有值的元素都是y结点的祖先结点,只需要查询x代表的子树中有多少个元素为1即可(这一段在DFS序中是连续的)。

注意DFS序是fail树的DFS序。DFS的是trie树。DFS序用树状数组维护即可,DFS的时候单点修改,计算答案的时候区间查询。

网上很多用链表的,我用了另外的方法就是直接对y排序然后并不是真正的DFS而是按照我们建立trie的顺序DFS,就可以按照顺序查询而不需要链表了,最终效果其实和DFS是一样的,只是不会返回根节点,但是返回途中根本就没有询问嘛。 (实际上我根本不会链表)

代码

说得比较轻巧,其实是需要码的。
但是不是很难,自从我刷BZOJa+b以后以来第一次1A哎。

#include<queue>
#include<cmath>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+105;
int n,m;
char s[maxn];
int l[maxn],r[maxn];
int ans[maxn],belong[maxn];//belong是第i个字符串对应的结点;
struct BIT
{
    int N,c[maxn+105];
    void Initial()
    {
        N=n+5;
        memset(c,0,sizeof(c));
    }
    int lowbit(int x)
    {
        return x&-x;
    }
    void add(int x,int v)
    {
        for(;x<=N;x+=lowbit(x))c[x]+=v;
    }
    int sum(int x)
    {
        int ret=0;
        for(;x;x-=lowbit(x))ret+=c[x];
        return ret;
    }
    int query(int l,int r)
    {
        return sum(r)-sum(l-1);
    }
}bit;
namespace fail_tree
{
    int dfn,np,first[maxn];
    struct edge{
        int to,next;
    }E[maxn];
    void add(int u,int v)
    {
        E[++np]=(edge){v,first[u]};
        first[u]=np;
    }
    void DFS(int i)
    {
        l[i]=++dfn;
        for(int p=first[i];p;p=E[p].next)DFS(E[p].to);
        r[i]=dfn;
    }
}
struct data{
    int x,y,id;
    friend bool operator<(data a,data b)
    {
        return a.y<b.y;
    }
}q[maxn];
struct AC_zidongji
{
    int sub,rt,np,ask,ch[maxn][30],fail[maxn],fa[maxn];
    void Initial()
    {
        sub=1;
        rt=np=ask=0;
        memset(ch,0,sizeof(ch));
    }
    int getId(char c)
    {
        return c-'a';
    }
    void ins()
    {
        for(int i=0,now=rt;s[i];i++)
        {
            if(s[i]=='P')
            {
                belong[++ask]=now;
            }
            else if(s[i]=='B')
            {
                now=fa[now];
            }
            else
            {
                int id=getId(s[i]);
                now=ch[now][id]?ch[now][id]:(fa[++np]=now,ch[now][id]=np);
            }
        }
    }
    void getFail()
    {
        queue<int>q;
        fail[rt]=0;
        for(int p=0;p<26;p++)
        {
            int j=ch[rt][p];
            if(j){fail[j]=0,fail_tree::add(fail[j],j),q.push(j);};
        }
        while(!q.empty())
        {
            int i=q.front();q.pop();
            for(int p=0;p<26;p++)
            {
                int j=ch[i][p];
                if(!j)continue;
                q.push(j);
                int v;
                for(v=fail[i];v && !ch[v][p];v=fail[v]);
                fail[j]=ch[v][p];
                fail_tree::add(fail[j],j);
            }
        }
    }
    void DFS()
    {
        for(int i=0,now=rt;s[i];i++)
        {
            if(s[i]=='B')
            {
                bit.add(l[now],-1);
                now=fa[now];
            }
            else if(s[i]!='P')
            {
                now=ch[now][getId(s[i])];
                bit.add(l[now],1);
            }
            while(sub<=m && belong[q[sub].y]==now)
            {
                ans[q[sub].id]=bit.query(l[belong[q[sub].x]],r[belong[q[sub].x]]);
                sub++;
            }
        }
    }
    void DFS(int i)
    {
        bit.add(l[i],1);
        while(sub<=m && belong[q[sub].y]==i)
        {
            ans[q[sub].id]=bit.query(l[q[sub].x],r[q[sub].x]);
            sub++;
        }
        for(int p=0;p<26;p++)
        {
            int j=ch[i][p];
            if(!j)continue;
        }
        bit.add(l[i],-1);
    }
}AC;
void Init()
{
    scanf("%s",s);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].x,&q[i].y);
        q[i].id=i;
    }
    sort(q+1,q+m+1);
    n=strlen(s);
    bit.Initial();
    AC.Initial();
    AC.ins();
    AC.getFail();
    fail_tree::DFS(0);
}
void solve()
{
    AC.DFS();
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
int main()
{
    //freopen("in.txt","r",stdin);
    Init();
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值