bzoj2555 SubString(LCT+SAM)

23 篇文章 0 订阅
16 篇文章 0 订阅

题目链接

分析:
翻了一下这道题的提交记录,发现yhzq和Mario_sz两个月前就做过了(希望现在填坑还不算晚)
第一次看这道题,感觉就是一道SAM
支持动态添加?SAM可以啊
匹配?SAM也可以啊

然而上网,得到都是 LCT+SAM L C T + S A M ,直接把我吓傻了
朴素的后缀自动机,每个节点维护一个 size s i z e ,添加一个字符时将它 parent p a r e n t size s i z e 都加一
询问时沿着后缀自动机走,输出最后一个节点的 size s i z e 即可

但暴力维护会TLE,于是只能上LCT了
注意不仅要 link l i n k 新加进来的节点,在后缀自动机构建时 parent p a r e n t 改变后也要及时 link l i n k cut c u t
但是这个LCT没有 makeroot m a k e r o o t 操作
因为 parent p a r e n t 树是有向的,根不能随便改

tip

不知道为什么老是RE
后来发现是cut出了点问题
因为C++中连等式是从右到左计算的,所以右边的写法才能保证正确

pre[ch[x][0]]=ch[x][0]=0;  --->  pre[ch[x][0]]=0; ch[x][0]=0;

T了一段时间
把函数放到struct里面就A了

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int N=1200005;
int mask;
char s[3000005];

void gets(int mask)
{
    scanf("%s",s);
    int len=strlen(s);
    for (int i=0;i<len;i++) {
        mask=(mask*131+i)%len;
        swap(s[i],s[mask]);
    }
}

struct lct{
    int ch[N][2],size[N],pre[N],tag[N];
    int q[N],top;

    int isroot(int bh) {
        return ch[pre[bh]][0]!=bh&&ch[pre[bh]][1]!=bh;
    }

    int get(int bh) {
        return (ch[pre[bh]][0]==bh? 0:1);
    }

    void push(int bh) {
        if (tag[bh]==0) return;
        if (ch[bh][0]) size[ch[bh][0]]+=tag[bh],tag[ch[bh][0]]+=tag[bh];
        if (ch[bh][1]) size[ch[bh][1]]+=tag[bh],tag[ch[bh][1]]+=tag[bh];
        tag[bh]=0;
    }

    void rotate(int bh) {
        int fa=pre[bh];
        int grand=pre[fa];
        int wh=get(bh);
        if (!isroot(fa)) ch[grand][ch[grand][0]==fa? 0:1]=bh;
        ch[fa][wh]=ch[bh][wh^1];
        pre[ch[fa][wh]]=fa;
        ch[bh][wh^1]=fa;
        pre[fa]=bh;
        pre[bh]=grand;
    }

    void splay(int bh) {
        int top=0;
        q[++top]=bh;
        for (int i=bh;!isroot(i);i=pre[i]) q[++top]=pre[i];
        while (top) push(q[top--]);
        for (int fa;!isroot(bh);rotate(bh))
            if (!isroot(fa=pre[bh]))
                rotate(get(bh)==get(fa) ? fa:bh);
    }

    void expose(int bh) {
        int t=0;
        while (bh) {
            splay(bh);
            ch[bh][1]=t;
            t=bh;
            bh=pre[bh];
        }
    }

    void link(int x,int y) {
        expose(y);
        splay(y);
        pre[x]=y;
        size[y]+=size[x];
        tag[y]+=size[x];
    }

    void cut(int x){
        expose(x);
        splay(x);
        size[ch[x][0]]-=size[x];
        tag[ch[x][0]]-=size[x];
        pre[ch[x][0]]=0;ch[x][0]=0;
    }       
}t;

struct sam{
    int dis[N],fa[N],ch[N][26];
    int sz,last;

    void insert(int x) {
        int pre=last,now=++sz;
        last=now;
        dis[now]=dis[pre]+1;
        for (;pre&&!ch[pre][x];pre=fa[pre]) ch[pre][x]=now;

        if (!pre) fa[now]=1;
        else {
            int q=ch[pre][x];
            if (dis[q]==dis[pre]+1)
                fa[now]=q;
            else {
                int nows=++sz;
                memcpy(ch[nows],ch[q],sizeof(ch[q]));
                dis[nows]=dis[pre]+1;
                fa[nows]=fa[q];
                t.link(nows,fa[nows]); t.cut(q);
                fa[q]=fa[now]=nows;
                t.link(q,nows);
                for (;pre&&ch[pre][x]==q;pre=fa[pre]) ch[pre][x]=nows;
            }
        }
        t.size[now]=1;
        t.link(now,fa[now]);
    } 

    void build(){
        last=++sz;
        scanf("%s",s);
        int l=strlen(s);
        for(int i=0;i<l;i++)
            insert(s[i]-'A');
    }

    void add(){
        gets(mask);
        int l=strlen(s);
        for(int i=0;i<l;i++)
            insert(s[i]-'A');
    }

    int solve(){
        gets(mask);
        int p=1,l=strlen(s);
        for(int i=0;i<l;i++)
            if(!(p=ch[p][s[i]-'A']))return 0;
        t.splay(p);
        return t.size[p];
    }
}sam;

int main()
{
    int Q;
    scanf("%d",&Q);
    sam.build();
    while(Q--)
    {
        scanf("%s",s);
        if(s[0]=='A') sam.add();
        else
        {
            int ans=sam.solve();
            printf("%d\n",ans);
            mask^=ans;
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值