BZOJ_2434

fail树上建可持久化树。

根据原题中给定的输入跑就可以跑出一颗trie树,然后对这个trie数建fail树。我最开始的思路错了:A串在B串中出现的次数就是看B中有多少节点可以直接或者说间接的指向A的子树。举个反例:A = aaa,设A的子树是一个点为b,B = aaab,那么B将会有两个指针指向A的子树,出错了。正确姿势应该是对fail树的DFS序修改,我的做法是可持久化,在AC自动机上的树跑DFS,每到一个点对它的fail的子树进行区间修改,查询的时候就查询子树和就好了(我真是太蠢了)

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100003;
char a[N];
int t1,t2,q,haha1,haha2,biao[N];
int first[N],to[N*2],next_[N*2],eg;
inline void addedge(int x,int y) {
    next_[++eg] = first[x];
    first[x] = eg;
    to[eg] = y;
}
struct AC {
    AC* fail;
    AC* ch[26];
    AC* pre;
    int key;
    AC() {
        for(int i = 0 ; i < 26 ; ++i) ch[i] = NULL;
        pre = fail = NULL;
        key = 0;
    }
}*R,dizhi1[N],*bfs[N];
struct tree1 {
    tree1* lson;
    tree1* rson;
    int sum;
    tree1() {
        lson = rson = NULL;
        sum = 0;
    }
}*root[N],dizhi2[N*20];
inline void get_fail() {
    bfs[1] = R;
    int h = 0 , t = 1;
    do {
        AC* u = bfs[++h];
        for(int i = 0 ; i < 26 ; ++i) {
            if(u->ch[i]==NULL) continue;
            if(u==R) {
                u->ch[i]->fail = R;
                addedge(R->key,u->ch[i]->key);
                bfs[++t] = u->ch[i];
                continue;
            }
            AC* temp = u->fail;
            while(temp!=R&&temp->ch[i]==NULL) temp = temp->fail;
            if(temp->ch[i]!=NULL) {
                u->ch[i]->fail = temp->ch[i];
                addedge(temp->ch[i]->key,u->ch[i]->key);
            } else {
                u->ch[i]->fail = R;
                addedge(R->key,u->ch[i]->key);
            }
            bfs[++t] = u->ch[i];
        }
    }while(h<t);
}
int st[N],ed[N],tt;
inline void dfs(int u) {
    st[u] = ++tt;
    for(int i = first[u] ; i ; i = next_[i])
        dfs(to[i]);
    ed[u] = tt;
}
inline void insert(tree1* tree,int l,int r,int pos,tree1* last) {
    if(l==r) {
        tree->sum = last==NULL?1:last->sum+1;
        return;
    }
    int mid = (l+r) >>1;
    if(pos <= mid) {
        if(last!=NULL && last->rson!=NULL) tree->rson = last->rson;
        tree->lson = &dizhi2[++t2];
        if(last!=NULL && last->lson!=NULL) insert(tree->lson,l,mid,pos,last->lson);
        else insert(tree->lson,l,mid,pos,NULL);
    } else {
        if(last!=NULL && last->lson!=NULL) tree->lson = last->lson;
        tree->rson = &dizhi2[++t2];
        if(last!=NULL && last->rson!=NULL) insert(tree->rson,mid+1,r,pos,last->rson);
        else insert(tree->rson,mid+1,r,pos,NULL);
    }
    if(tree->lson!=NULL) tree->sum += tree->lson->sum;
    if(tree->rson!=NULL) tree->sum += tree->rson->sum;
}
inline void build(AC* u) {
    if(u!=R) {
        root[u->key] = &dizhi2[++t2];
        insert(root[u->key],1,haha2,st[u->key],root[u->pre->key]);
    }
    for(int i = 0 ; i < 26 ; ++i) 
        if(u->ch[i]!=NULL) build(u->ch[i]);
}
inline int query(tree1* tree,int l,int r,int x,int y) {
    if(x<=l && r<=y) return tree->sum;
    int mid = (l+r)>>1,q1 = 0,q2 = 0;
    if(!(y<l || mid<x) && tree->lson!=NULL) q1 = query(tree->lson,l,mid,x,y);
    if(!(y<mid+1||r<x) && tree->rson!=NULL) q2 = query(tree->rson,mid+1,r,x,y);
    return (q1+q2);
}
int main() {
    scanf("%s",a);
    int len = strlen(a);
    R = &dizhi1[++t1];
    R->key = ++haha2;
    AC* now = R;
    for(int i = 0 ; i < len ; ++i) {
        if(a[i]=='B') {
            now = now->pre;
            continue;
        }
        if(a[i]=='P') {
            biao[++haha1] = now->key;
            continue;
        }
        int to = a[i] - 'a';
        if(now->ch[to]==NULL) {
            now->ch[to] = &dizhi1[++t1];
            now->ch[to]->pre = now;
            now->ch[to]->key = ++haha2;
        }
        now = now->ch[to];
    }
    get_fail();
    dfs(R->key);
    build(R);
    scanf("%d",&q);
    for(int i = 1,x,y ; i <= q ; ++i) {
        scanf("%d%d",&x,&y);
        printf("%d\n",query(root[biao[y]],1,haha2,st[biao[x]],ed[biao[x]]));
    }
}
View Code

 

转载于:https://www.cnblogs.com/registerzxr/p/5081774.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值