bzoj2434 ac自动机+fail树+Dfs序+树状数组

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2434
大意:有3种操作,1、在末尾加入一个字符;2、删除末尾的一个字符;3、打印当前字符串。
m个询问,第a个打印出来的字符串在第b个打印出来的字符串中出现了几次。

首先建立AC自动机,第2个操作相当于跳到当前节点的父亲,第3个操作可以挂到节点上。
然后我们来研究询问。
可以发现,若a字符串出现在b字符串中,那通过b的fail肯定可以找到a。
这样我们可以将fail反向,建一棵fail树。一个字符串a出现的次数就是它在fail树中子树节点的值。
对于每个询问,我们照样把a挂到b上。
现在的问题是:支持节点修改,询问子树值。
那就直接Dfs序+树状数组就好啦。(都是好神的题,还是太弱了。。。)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct sef { int ne,en; }h[200050];
int tot,first[200050];

struct trie {
    int son[27],fa,fail;
}T[200050];
int q[200050],ql;
int len,sum,Id[200050],num,pos[200050];
int To[200050],tim,siz[200050],ans[200050];
int d[200050];
int m;
char s[200050];
inline void setl(int x,int y)
{
    h[++tot].ne=first[x]; h[tot].en=y;
    first[x]=tot;
}
inline void Pre_Trie()
{
    sum=1;  int x=1,y;
    for (int i=0;i<len;++i) {
        if (s[i]=='P') { pos[++num]=x; Id[x]=num; }
        else if (s[i]=='B') x=T[x].fa;
        else {
            y=s[i]-'a';
            if (!T[x].son[y]) T[x].son[y]=++sum;
            T[T[x].son[y]].fa=x;
            x=T[x].son[y];
        }   
    }
}
inline void Pre_Fail()
{
    for (int i=0;i<26;++i) T[0].son[i]=1;
    q[ql=1]=1;
    int x,cur,y;
    for (int i=1;i<=ql;++i) {
        x=q[i];
        for (int j=0;j<26;++j) 
            if (y=T[x].son[j]) {
                cur=T[x].fail;
                while (!T[cur].son[j]) cur=T[cur].fail;
                cur=T[cur].son[j];
                T[y].fail=cur;
                setl(cur,y);
                q[++ql]=y;
            }
    }
}
inline void Dfs(int x)
{
    To[x]=++tim;
    siz[x]=1;
    for (int i=first[x];i;i=h[i].ne) {
        Dfs(h[i].en);
        siz[x]+=siz[h[i].en];
    }
    //Out[x]=++tim;
}
inline void Add(const int &x,int v)
{
    for (int i=To[x];i<=sum;i+=i & (-i)) d[i]+=v;
    //for (int i=To[x]+siz[x];i<=sum;i+=i & (-i)) d[i]-=v;
}
inline int Ask(int x)
{
    int r=0;
    for (int i=To[x]+siz[x]-1;i;i-=i & (-i)) r+=d[i];
    for (int i=To[x]-1;i;i-=i & (-i)) r-=d[i];
    return r;
}
inline void Query(int k)
{
    int x=Id[k];
    int y;
    for (int i=first[x];y=h[i].en,i;i=h[i].ne)
        ans[i]=Ask(pos[y]);
}
inline void Work()
{
    int x=1,y;
    for (int i=0;i<len;++i) {
        if (s[i]=='P') Query(x);
        else if (s[i]=='B') { Add(x,-1); x=T[x].fa; }
        else {
            y=s[i]-'a';
            x=T[x].son[y]; Add(x,1);
        }
    }
}
int main()
{
    scanf("%s",s);
    len=strlen(s);
    Pre_Trie();
    Pre_Fail();
    Dfs(1);
    memset(first,0,sizeof(first));
    tot=0;
    scanf("%d",&m);
    int x,y;
    for (int i=1;i<=m;++i) {
        scanf("%d%d",&x,&y);
        setl(y,x);
    }
    Work();
    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值