BZOJ 2434: [Noi2011]阿狸的打字机 AC自动机 fail树

5 篇文章 0 订阅
1 篇文章 0 订阅

一道AC自动机好题啊,做完了以后对AC自动机有了一个新的认识,首先由于这道题的特殊性我们无需一个一个字符串插入,只要维护一个father指针往回跳就行了,建好树了以后怎么办呢,我们可以想到A在AC自动机上是B的子串当且仅当B的节点跳fail指针可以跳到A,但是这样时间复杂度太大,由于fail指针总是由下向上指所以我们能想得到如果将fail倒过来那么将形成一颗树,我们称之为fail树,那么B的节点跳fail能跳到A等价于B的节点在A的子树中,我们只需维护A的子树中有多少B的节点就好了,我们只要维护一下dfs序就可快速得知子树信息,用树状数组就可以区间求和,由于这道题的特殊性,将询问按照y值排序,我们只需再跑一边字符串就可nlogn维护树状数组,这样这道题就解决了
1A ^_^

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
struct Trie
{
    Trie *son[26],*fail,*fa;
    int num;
    int wz;
    Trie()
    {
        memset(son,0,sizeof(son));
        fa=fail=NULL;
        num=0;
    }
}mempool[1000000],*root=&mempool[0];
int toppp=0;
int top=0;
void my_insert(char *s)
{
    root->fa=root;
    Trie *o=root;
    while(*s)
    {
        if(*s=='B') o=o->fa;
        else if(*s=='P') o->num=++top;
        else
        {
            if(!o->son[*s-'a'])
            {
                o->son[*s-'a']=&mempool[++toppp];
                o->son[*s-'a']->fa=o;
            }
            o=o->son[*s-'a'];
        }
        s++;
    }
}
struct edge
{
    int l,r;
}a[1000000];
int fir[1000000];
int nex[1000000];
int tot=0;
void add_edge(int l,int r)
{
    a[++tot].l=l;
    a[tot].r=r;
    nex[tot]=fir[l];
    fir[l]=tot;
}
void bfs()
{
    static Trie* dui[1000000];
    int top=1,my_final=1;
    for(int i=0;i<26;i++)
    {
        if(root->son[i])
        {
            root->son[i]->fail=root;
            add_edge(root->wz,root->son[i]->wz);
            dui[my_final++]=root->son[i];
        }
        else  root->son[i]=root;
    }
    while(top<my_final)
    {
        Trie *o=dui[top++];
        for(int i=0;i<26;i++)
        {
            if(o->son[i])
            {
                dui[my_final++]=o->son[i];
                o->son[i]->fail=o->fail->son[i];
                add_edge(o->fail->son[i]->wz,o->son[i]->wz);
            }
            else o->son[i]=o->fail->son[i];
        }
    }
}

int dfsx[1000000];
int wz[1000000];
int tott=0;
int my_begin[1000000];
int my_end[1000000];
int c[1000000];
int mapp[1000000];
void dfs(int u,int from)
{
    dfsx[++tott]=u;
    my_begin[u]=tott;
    if(mempool[u].num) mapp[mempool[u].num]=u;
    for(int o=fir[u];o!=0;o=nex[o])
        if(a[o].r!=from) dfs(a[o].r,u);
    dfsx[++tott]=u;
    my_end[u]=tott;
}
struct query
{
    int x,y,num;
    bool operator <(query b) const
    {
        return y<b.y;
    }
}queries[1000000];
int ans[1000000];
inline void add_v(int x,int v)
{
    for(int i=x;i<=tott;i+=i&-i) c[i]+=v;
}
inline int my_search(int l,int r)
{
    int re=0;
    for(int i=r;i;i-=i&-i) re+=c[i];
    for(int i=l-1;i;i-=i&-i) re-=c[i];
    return re;
}
int pipei=1;
void search_ans(char *s)
{
    Trie *o=root;
    add_v(my_begin[root->wz],1);
    while(*s)
    {
        if(*s=='B')
        {
            add_v(my_begin[o->wz],-1);
            o=o->fa;
        }
        else if(*s=='P')
        {
            while(queries[pipei].y==o->num)
            {
                ans[queries[pipei].num]=my_search(my_begin[mapp[queries[pipei].x]],my_end[mapp[queries[pipei].x]]);
                pipei++;
            }
        }
        else
        {
            o=o->son[*s-'a'];
            add_v(my_begin[o->wz],1);
        }
        s++;
    }
}
char s[1000000];
int main()
{
    scanf("%s",s);
    my_insert(s);
    for(int i=0;i<=toppp;i++) mempool[i].wz=i;
    bfs();
    dfs(0,-1);
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&queries[i].x,&queries[i].y),queries[i].num=i;
    sort(queries+1,queries+1+m);
    search_ans(s);
    for(int i=1;i<=m;i++)
    {
        printf("%d\n",ans[i]);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值