【bzoj2010】SubString【后缀自动机+LCT】

题意:

有2个操作。
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。

题解:

建后缀自动机。cnt表示当前状态字符串出现的次数。
每插入一个字符串,就把它插进sam里面,再把往上跳fail走到的节点的cnt值+1。因为当前状态表示的字符串,在fail链上的所有状态肯定也出现了。
每次查询,顺着sam的边走,最后走到的节点的cnt就是答案。
可以想到,把fail链倒过来之后形成的一定是一棵树。所以链上加,直接用link-cut tree维护即可。
注意单点修改后一定要再LCT上splay,access,或者makeroot一下。而且mask穿进解码函数是一个形参!不要直接修改了!

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=600005;
int m,l,ans,mask;
char op[10],s[N*5];
void decode(int mm){
    for(int i=0;i<l;i++){
        mm=(mm*131+i)%l;
        swap(s[i],s[mm]);
    }
}
struct LCT{
    int cnt,ch[N*2][2],fa[N*2],val[N*2],rev[N*2],tag[N*2],stk[N*2];
    bool isroot(int u){
        return u!=ch[fa[u]][0]&&u!=ch[fa[u]][1];
    }
    int which(int u){
        return u==ch[fa[u]][1];
    }
    void reverse(int u){
        if(!u){
            return;
        }
        rev[u]^=1;
        swap(ch[u][0],ch[u][1]);
    }
    void add(int u,int v){
        if(!u){
            return;
        }
        tag[u]+=v;
        val[u]+=v;
    }
    void downtag(int u){
        if(rev[u]){
            reverse(ch[u][0]);
            reverse(ch[u][1]);
            rev[u]=0;
        }
        if(tag[u]){
            add(ch[u][0],tag[u]);
            add(ch[u][1],tag[u]);
            tag[u]=0;
        }
    }
    void pushdown(int u){
        stk[stk[0]=1]=u;
        for(;!isroot(u);u=fa[u]){
            stk[++stk[0]]=fa[u];
        }
        while(stk[0]){
            downtag(stk[stk[0]--]);
        }
    }
    void rotate(int x){
        int y=fa[x],md=which(x);
        if(!isroot(y)){
            ch[fa[y]][which(y)]=x;
        }
        fa[x]=fa[y];
        ch[y][md]=ch[x][!md];
        fa[ch[y][md]]=y;
        ch[x][!md]=y;
        fa[y]=x;
    }
    void splay(int u){
        pushdown(u);
        while(!isroot(u)){
            if(!isroot(fa[u])){
                rotate(which(u)==which(fa[u])?fa[u]:u);
            }
            rotate(u);
        }
    }
    void access(int u){
        for(int v=0;u;v=u,u=fa[u]){
            splay(u);
            ch[u][1]=v;
        }
    }
    void makeroot(int u){
        access(u);
        splay(u);
        reverse(u);
    }
    void link(int u,int v){
        makeroot(u);
        fa[u]=v;
    }
    void cut(int u,int v){
        makeroot(u);
        access(v);
        splay(v);
        fa[u]=ch[v][0]=0;
    }
}lct;
struct Sam{
    int last,cnt,ch[N*2][26],fa[N*2],len[N*2],siz[N*2];
    Sam(){
        last=cnt=1;
    }
    void insert(int x){
        int p=last,np=++cnt;
        last=np;
        len[np]=len[p]+1;
        for(;p&&!ch[p][x];p=fa[p]){
            ch[p][x]=np;
        }
        if(!p){
            fa[np]=1;
            lct.link(np,1);
        }else{
            int q=ch[p][x];
            if(len[q]==len[p]+1){
                fa[np]=q;
                lct.link(np,q); 
            }else{
                int nq=++cnt;
                len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];
                lct.link(nq,fa[nq]);
                lct.cut(q,fa[q]);
                fa[q]=fa[np]=nq;
                lct.link(q,fa[q]);
                lct.link(np,fa[np]);
                lct.pushdown(q);
                lct.val[nq]=lct.val[q];
                lct.access(nq);
                for(;ch[p][x]==q;p=fa[p]){
                    ch[p][x]=nq;
                }
            }
        }
        lct.makeroot(1);
        lct.access(np);
        lct.splay(np);
        lct.add(np,1);
    }
    int query(){
        int k=1;
        for(int i=0;i<l;i++){
            if(!ch[k][s[i]-'A']){
                return 0;
            }
            k=ch[k][s[i]-'A'];
        }
        lct.access(k);
        return lct.val[k];
    }
}sam;
int main(){
    scanf("%d%s",&m,s);
    l=strlen(s);
    for(int i=0;i<l;i++){
        sam.insert(s[i]-'A');
    }
    while(m--){
        scanf("%s%s",op,s);
        l=strlen(s);
        decode(mask);
        if(op[0]=='A'){
            for(int i=0;i<l;i++){
                sam.insert(s[i]-'A');
            }
        }else{
            ans=sam.query();
            printf("%d\n",ans);
            mask^=ans;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值