bzoj2555 LCT维护后缀自动机

通过用LCT维护parent树来实现后缀自动机的在线操作。

注意right值初始化为0,然后加新结点的时候只要将np的right值设为1,而不需要改变nq的right值,因为nq是内部的结点,np才是外层的结点。

思路很简单,代码真长,调了挺久。。。。不过写起来还算清晰。。。

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=3200100;
const int INF=1e9+10;

char s[maxn];
int len;
char op[20];
int Q;

struct LCT
{
    int pre[maxn],ch[maxn][2],rev[maxn];
    int val[maxn],add[maxn];
    void init()
    {
        MS0(pre);MS0(ch);MS0(rev);MS0(add);
        MS0(val);
    }
    void update_add(int x,int w)
    {
        if(!x) return;
        val[x]+=w;
        add[x]+=w;
    }
    void update_rev(int x)
    {
        if(!x) return;
        swap(ch[x][0],ch[x][1]);
        rev[x]^=1;
    }
    void down(int x)
    {
        if(add[x]){
            update_add(ch[x][0],add[x]);
            update_add(ch[x][1],add[x]);
            add[x]=0;
        }
        if(rev[x]){
            update_rev(ch[x][0]);
            update_rev(ch[x][1]);
            rev[x]=0;
        }
    }
    bool isroot(int x)
    {
        return ch[pre[x]][0]!=x&&ch[pre[x]][1]!=x;
    }
    void up(int x)
    {

    }
    void P(int x)
    {
        if(!isroot(x)) P(pre[x]);
        down(x);
    }
    void rot(int x,int kind)
    {
        int y=pre[x];
        ch[y][kind^1]=ch[x][kind];
        pre[ch[x][kind]]=y;
        if(!isroot(y)) ch[pre[y]][ch[pre[y]][1]==y]=x;
        pre[x]=pre[y];
        ch[x][kind]=y;
        pre[y]=x;
        up(y);
    }
    void splay(int x)
    {
        P(x);
        while(!isroot(x)){
            if(isroot(pre[x])) rot(x,ch[pre[x]][0]==x);
            else{
                int y=pre[x],z=pre[y];
                int kind=ch[y][0]==x,one=0;
                if(ch[y][0]==x&&ch[z][0]==y) one=1;
                if(ch[y][1]==x&&ch[z][1]==y) one=1;
                if(one) rot(y,kind),rot(x,kind);
                else rot(x,kind),rot(x,kind^1);
            }
        }
        up(x);
    }
    int access(int x)
    {
        int t=0;
        while(x){
            splay(x);
            ch[x][1]=t;t=x;x=pre[x];
            up(t);
        }
        return t;
    }
    void makeroot(int x)
    {
        access(x);splay(x);update_rev(x);
    }
    void ADD(int x,int y,int w)
    {
        makeroot(x);access(y);splay(y);
        update_add(y,w);
    }
    void cut(int x,int y)
    {
        makeroot(x);access(y);splay(y);ch[y][0]=pre[x]=0;up(y);
        ADD(y,1,-val[x]);
    }
    void link(int x,int y)
    {
        makeroot(x);pre[x]=y;up(y);
        ADD(y,1,val[x]);
    }
    int query(int x)
    {
        splay(x);
        return val[x];
    }
};LCT lct;

struct SAM
{
    int ch[maxn][26];
    int pre[maxn],step[maxn];
    int last,tot;
    int newnode(int k)
    {
        ++tot;
        step[tot]=k;
        MS0(ch[tot]);
        pre[tot]=0;
        return tot;
    }
    void init()
    {
        tot=0;
        last=newnode(0);
        lct.init();
    }
    void add(int c)
    {
        c-=‘A‘;
        int p=last,np=newnode(step[p]+1);
        lct.val[np]=1;
        while(p&&ch[p][c]==0) ch[p][c]=np,p=pre[p];
        if(p==0) pre[np]=1,lct.link(np,1);
        else{
            int q=ch[p][c];
            if(step[q]!=step[p]+1){
                int nq=newnode(step[p]+1);
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                lct.cut(q,pre[q]);
                lct.link(nq,pre[q]);
                lct.link(q,nq);
                lct.link(np,nq);
                pre[nq]=pre[q];
                pre[q]=pre[np]=nq;
                while(p&&ch[p][c]==q) ch[p][c]=nq,p=pre[p];
            }
            else pre[np]=q,lct.link(np,q);
        }
        last=np;
    }
    int find(char *s)
    {
        int len=strlen(s);
        int u=1;
        REP(i,0,len-1){
            int c=s[i]-‘A‘;
            if(ch[u][c]) u=ch[u][c];
            else return 0;
        }
        return lct.query(u);
    }
};SAM sam;

void decode(char *s,int mask)
{
    int len=strlen(s);
    REP(i,0,len-1){
        mask=(mask*131+i)%len;
        swap(s[i],s[mask]);
    }
}

int main()
{
    freopen("in.txt","r",stdin);
    while(~scanf("%d",&Q)){
        scanf("%s",s);
        len=strlen(s);
        sam.init();
        REP(i,0,len-1) sam.add(s[i]);
        int mask=0;
        REP(i,1,Q){
            scanf("%s%s",op,s);
            decode(s,mask);
            if(op[0]==‘A‘){
                len=strlen(s);
                REP(i,0,len-1) sam.add(s[i]);
            }
            else{
                int res=sam.find(s);
                printf("%d\n",res);
                mask^=res;
            }
        }
    }
    return 0;
}

时间: 2024-10-11 00:44:29

bzoj2555 LCT维护后缀自动机的相关文章

【BZOJ2555】SubString 后缀自动机+LCT

[BZOJ2555]SubString Description 懒得写背景了,给你一个字符串init,要求你支持两个操作         (1):在当前字符串的后面插入一个字符串         (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)         你必须在线支持这些操作. Input 第一行一个数Q表示操作个数         第二行一个字符串表示初始字符串init         接下来Q行,每行2个字符串Type,Str         Type是ADD的话表示在

【BZOJ-2555】SubString 后缀自动机 + LinkCutTree

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1936  Solved: 551[Submit][Status][Discuss] Description 懒得写背景了,给你一个字符串init,要求你支持两个操作    (1):在当前字符串的后面插入一个字符串    (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)    你必须在线支持这些操作. Input 第一行一个数Q表示操作个数    第二行一

【BZOJ2555】SubString 后缀自动机 暴力

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42915143 题解:正解是LCT+SAM. 但是出题人太神以至于不屑于卡你,所以只写个SAM然后暴力维护就好了. 这样虽然很不科学,但是可以比正解快三倍. 我是太困了吧,写挂了调了好久(发呆了好久然后秒调过,或者说半眯着眼睛睡了一会) 代码: <span style="font-family:KaiTi_GB2312;font-size:18px;">#includ

字符串(LCT,后缀自动机):BZOJ 2555 SubString

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1620  Solved: 471 Description 懒得写背景了,给你一个字符串init,要求你支持两个操作          (1):在当前字符串的后面插入一个字符串          (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)          你必须在线支持这些操作. Input 第一行一个数Q表示操作个数          第二行一个

bzoj2555(后缀自动机+LCT)

题目描述 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支持这些操作. 题解 做法很自然,建出后缀自动机,维护每个节点的right集合,对于询问直接在sam上跑就好了. 然后它是在线的,得用LCT维护. 然后细节极多,首先必须维护好树的形态,也就是说不能makeroot,所以我的link就长这样. inline void link(int x,int y){ access(x);splay(x);access(y);splay

【BZOJ2555】SubString(后缀自动机,Link-Cut Tree)

[BZOJ2555]SubString(后缀自动机,Link-Cut Tree) 题面 BZOJ 题解 这题看起来不难 每次要求的就是\(right/endpos\)集合的大小 所以搞一个\(LCT\)维护一下\(SAM\)的\(Parent\)树就好了 但是代码一点都不好写(我还是对着黄学长的调的...) 于是乎我也学着魔改了一下\(LCT\) #include<iostream> #include<cstdio> #include<cstdlib> #include

51nod 1600 Simplr KMP(后缀自动机+维护树上的数据结构)

题意:对于每个位置,统计有多少个相同的字串. 分析:按照题目的意思,把fail树画出来就会发现,对于第i个字符:ans[i] = ans[i-1] + (ans[i-1]-ans[i-1]) + cal(i); cal(i)是计算s[1…i-1]所有子串与s[1…i]的最长公共后缀的和.换句话说,根据后缀自动机性质,沿着parent树往上走可以知道对于后缀s[1…i]的所有位置的公共后缀长度以及个数(right集合的大小).很容易可以计算出cal(i), 只要每次新增一个字符的时候,在paren

bzoj 2555 SubString —— 后缀自动机+LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 建立后缀自动机,就可以直接加入新串了: 出现次数就是 Right 集合的大小,需要查询 Parent 树上的子树和: 所以可以用 LCT 维护 Parent 树,因为 Parent 树是有根树所以不需要 makeroot: 代码中的两种 cut 写法都可以,其实这里的 splay 节点上记的 siz 值不是 splay 子树里的而是原子树( Parent 树上)里的: 注意读入的函数

【BZOJ4545】DQS的trie 后缀自动机+LCT

[BZOJ4545]DQS的trie Description DQS的自家阳台上种着一棵颗粒饱满.颜色纯正的trie. DQS的trie非常的奇特,它初始有n0个节点,n0-1条边,每条边上有一个字符.并且,它拥有极强的生长力:某个i时刻,某个节点就会新生长出一颗子树,它拥有si个节点且节点之间的边上有一个字符,并且新生长出来的子树也是一个树结构.然而因为是新长出来的,根据生活常识可知si必定不会大于i时刻之前的树的大小. DQS定义trie的子串为从根节点(1号节点)往下走到所有节点所构成的字