BZOJ2434: [Noi2011]阿狸的打字机

41 篇文章 0 订阅
12 篇文章 0 订阅

阿狸的打字过程实际上是在建一个trie树,对这棵trie建立ac自动机的fail树后,问第x个串在第y个串中出现了多少次,相当于问y串有多少个前缀,在fail树中位于x的子树里
到这里其实好像离线一下就可以了?但还有另一种资瓷在线询问的做法
我们不可能对y串的每个前缀处理,因为这个trie树的建立可以让所有串的总长是n^2级别的
但其实在trie树上这样走造字符串,很多串有着很长的LCP,比如在t1时刻我从节点x走了下去,在t2时刻退出了节点x,那么t1~t2时刻造出的所有字符串的前缀都有x,我们在trie树上走进一个节点时记录当前在造第x个串,离开时若当前在造第y个串,那么x就是串[x,y)的前缀,x的子树中[x,y)的出现次数就+1,这样得到的区间[x,y)的数量是O(n)个的,对每个点p的[x,y),在p的线段树上对[x,y)区间+1,然后在fail树上线段树合并,就可以求出每个点p的子树内每个串的出现次数,就可以O(log)做到在线询问了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;

int n,m,to[maxn];
int ans[maxn],qi[maxn];
vector<int>query[maxn];
struct segment
{
    int seg[maxn*25],lc[maxn*25],rc[maxn*25],root[maxn],cnt;
    int lx,rx;
    void add(int &x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(!x) x=++cnt;
        if(lx<=l&&r<=rx) { seg[x]++; return; }
        int mid=l+r>>1;
        add(lc[x],l,mid); add(rc[x],mid+1,r);
    }
    void merge(int &x,const int y)
    {
        if(!y) return;
        if(!x) { x=y; return; }
        seg[x]+=seg[y];
        merge(lc[x],lc[y]);
        merge(rc[x],rc[y]);
    }
    int loc,c;
    int query(const int x,const int l,const int r)
    {
        c+=seg[x];
        if(l==r||!x) return c;
        int mid=l+r>>1;
        if(loc<=mid) return query(lc[x],l,mid);
        else return query(rc[x],mid+1,r);
    }
}seg;
struct interval{int l,r;};
vector<interval>V[maxn];

struct edge{int y,nex;}a[maxn]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
void dfs(const int x)
{
    for(int i=0;i<V[x].size();i++)
    {
        const interval now=V[x][i];
        seg.lx=now.l,seg.rx=now.r-1;
        if(seg.lx<=seg.rx) seg.add(seg.root[x],1,n);
    }
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
    {
        dfs(y);
        seg.merge(seg.root[x],seg.root[y]);
    }
    for(int i=0;i<query[x].size();i++)
    {
        int j=query[x][i],y=qi[j];
        seg.loc=y; seg.c=0;
        ans[j]=seg.query(seg.root[x],1,n);
    }
}

struct trie{int son[26],fa,fail,las;}tr[maxn]; int root,tot;

char str[maxn];
queue<int>q;
void build_trie()
{
    int x=root,len=strlen(str); n=1;
    for(int i=0;i<len;i++)
    {
        char cc=str[i];
        if(cc=='P') to[++n]=x;
        else if(cc=='B') V[x].push_back((interval){tr[x].las,n}),x=tr[x].fa;
        else
        {
            int c=cc-'a';
            if(!tr[x].son[c]) tr[tr[x].son[c]=++tot].fa=x;
            x=tr[x].son[c]; tr[x].las=n;
        }
    }
    while(1)
    {
        V[x].push_back((interval){tr[x].las,n});
        if(x==root) break;
        x=tr[x].fa;
    }
    q.push(root);
    while(!q.empty())
    {
        const int x=q.front(); q.pop();
        int fl=tr[x].fail;
        for(int i=0;i<26;i++)
        {
            int &y=tr[x].son[i];
            if(y)
            {
                if(x!=root) tr[y].fail=tr[fl].son[i];
                q.push(y);
            }
            else y=tr[fl].son[i];
        }
    }
}
void build_failtree()
{
    len=0; //memset(fir,0,sizeof fir);
    for(int i=1;i<=tot;i++) ins(tr[i].fail,i);
}

int main()
{
    scanf("%s",str);
    build_trie();
    build_failtree();

    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        qi[i]=y;
        query[to[x+1]].push_back(i);
    }
    dfs(0);
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值