BZOJ2555 SubString

题意

要求你在线支持下面几个操作:
1.在当前字符串末尾插入一个字符串
2.查询一个字符串作为子串在当前字符串中出现了多少次

题解

后缀自动机
显然出现次数就是对应节点right大小对吧
那么动态维护怎么做呢?
LCT
再详细的讲讲怎么用LCT维护fail树(好像也叫parent树来着)吧(此处注意LCT并不完全是正常的LCT)
简单的讲,就是正常的SAM新建节点时在LCT新建节点,更新后缀链接时同时在LCTcut原来的边并link新边
而怎么进行更新right大小呢
我们储存两个信息,val和tag,val就用来保存答案,tag是懒标记
每次加上新编边时,我们Access fa,Splay fa,其实就差个翻转就跟makeroot一样了
但是注意,fail树的形态是固定的,也就是说,我们不能翻转,然后我们把这个节点的贡献上传,打上懒标记
所以这个tag的作用就很明显了,其实就是用来更新这个更改点到根的路径上的点的val
cut的时候也是类似的,减去对应的贡献

一堆细节
样例爆水…建议自己先测点

2
AB
ADD BBABB
QUERY BB
ans:3

那个mask是形参不是引用…
找对应节点时如果已经没有了转移,直接返回0(不要走fail…)
不要像我一样傻傻的每次插入写了个last=root
我震惊了…字符集开的2过了…

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
const int N=3000000+5,T=3000000+5;
const int INF=1<<30;
typedef long long ll;
char s[N];
    struct node *NIL;
    struct node{
        int tag,val,sz;
        node *fa,*ch[2];
        int Dir(){
            return this==fa->ch[1];
        }
        bool Isroot(){
            return fa==NIL||(this!=fa->ch[0]&&this!=fa->ch[1]);
        }
        void Setchild(node *x,int d){
            ch[d]=x;
            if(x!=NIL)
                x->fa=this;
        }
        void Pushup(){
            sz=ch[0]->sz+ch[1]->sz+1;
        }
        void Pushdown(){
            if(tag){
                if(ch[0]!=NIL){
                    ch[0]->tag+=tag;
                    ch[0]->val+=tag;
                }
                if(ch[1]!=NIL){
                    ch[1]->tag+=tag;
                    ch[1]->val+=tag;
                }
                tag=0;
            }
        }
    }tree[T],*tcnt;
    void Debug(){
        for(int i=1;tree+i<=tcnt;i++)
            printf("%d lch:%d rch:%d fa:%d val:%d tag:%d\n",i,(tree+i)->ch[0]-tree,(tree+i)->ch[1]-tree,(tree+i)->fa-tree,(tree+i)->val,(tree+i)->tag);
    }
    void Init(){
        tcnt=NIL=&tree[0];
        NIL->fa=NIL->ch[0]=NIL->ch[1]=NIL;
    }
    node *Newnode(int val){
        tcnt++;
        tcnt->val=val,tcnt->sz=1;
        tcnt->ch[0]=tcnt->ch[1]=tcnt->fa=NIL;
        return tcnt;
    }
    void Rotate(node *x){
        node *y=x->fa;
        y->Pushdown();
        x->Pushdown();
        int d=x->Dir();
        if(y->Isroot())
            x->fa=y->fa;
        else
            y->fa->Setchild(x,y->Dir());
        y->Setchild(x->ch[!d],d);
        x->Setchild(y,!d);
        y->Pushup();
    }
    void Splay(node *x){
        x->Pushdown();
        while(!x->Isroot()){
            node *y=x->fa;
            if(y->Isroot())
                Rotate(x);
            else{
                if(x->Dir()^y->Dir())
                    Rotate(x);
                else
                    Rotate(y);
                Rotate(x);
            }
        }
        x->Pushup();
    }
    void Access(node *x){
        node *y=NIL;
        while(x!=NIL){
            Splay(x);
            x->Setchild(y,1);
            x->Pushup();
            y=x;
            x=x->fa;
        }
    }
    void Link(node *x,node *y){
        Access(y);
        Splay(y);
        x->fa=y;
        y->tag+=x->val;
        y->val+=x->val;
    }
    void Cut(node *x){
        Access(x);
        Splay(x);
        x->ch[0]->tag-=x->val;
        x->ch[0]->val-=x->val;
        x->ch[0]->fa=NIL;
        x->ch[0]=NIL;
    }
class SAM{
public:
    struct mode{
        mode *fail;
        mode *ch[26];
        int len;
    }a[T],*last,*tcnt,*root;
    void Debug(){
        for(int i=0;root+i<=tcnt;i++){
            printf("%d:\n",i);
            for(int j=0;j<26;j++)
                if((root+i)->ch[j])
                    printf("%c->%d\n",'A'+j,(root+i)->ch[j]-root);
            if(i)
                printf("fa: %d\n",(root+i)->fail-root);
            printf("len:%d\n",(root+i)->len);
        }
    }
    node *z[T];
    void Init(){
        last=tcnt=root=&a[0];
        z[0]=Newnode(0);
    }
    void Link(mode *p,mode *q){
        ::Link(z[p-root],z[q-root]);
        //::Debug();
    }
    void Cut(mode *p){
        ::Cut(z[p-root]);
        //::Debug();
    }
    void Insert(char *s){
        int n=strlen(s);
        //last=root;
        for(int i=0;i<n;i++){
            int c=s[i]-'A';
            mode *p=last,*np=++tcnt,*q,*nq;
            z[np-root]=Newnode(1);
            last=np;
            np->len=p->len+1;
            while(p&&!p->ch[c])
                p->ch[c]=np,p=p->fail;
            if(!p){
                np->fail=root;
                Link(np,root);
            }
            else{
                q=p->ch[c];
                if(q->len==p->len+1){
                    np->fail=q;
                    Link(np,q);
                }
                else{
                    nq=++tcnt;
                    z[nq-root]=Newnode(0);
                    *nq=*q;
                    nq->len=p->len+1;
                    Link(nq,q->fail);
                    q->fail=nq;
                    np->fail=nq;
                    Cut(q);
                    Link(q,nq);
                    Link(np,nq);
                    while(p&&p->ch[c]==q)
                        p->ch[c]=nq,p=p->fail;
                }
            }
        }
    }
    int Match(char *s){
        int n=strlen(s);
        mode *p=root;
        for(int i=0;i<n;i++){
            int c=s[i]-'A';
            if(p->ch[c])
                p=p->ch[c];
            else
                return 0;
        }
        Splay(z[p-root]);
        return z[p-root]->val;
    }
}sam;
int mask=0;
void decodeWithMask(int mask){
    int n=strlen(s);
    for(int j=0;j<n;j++){
        mask=(mask*131+j)%n;
        swap(s[j],s[mask]);
    }
}
int main()
{
    Init();
    sam.Init();
    int q;
    scanf("%d",&q);
    scanf("%s",s);
    sam.Insert(s);
    //Debug();
    //sam.Debug();
    for(int i=1;i<=q;i++){
        scanf("%s",s);
        if(strcmp(s,"ADD")==0){
            scanf("%s",s);
            decodeWithMask(mask);
            sam.Insert(s);
            //Debug();
            //sam.Debug();
        }
        else{
            scanf("%s",s);
            decodeWithMask(mask);
            int res=sam.Match(s);
            mask^=res;
            printf("%d\n",res);
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值