BZOJ 2555 Substring 后缀自动机+Link-Cut-Tree

题目大意:给定一个初始字符串,提供两种操作:

1.在这个字符串的后面连接一个字符串

2.询问某个字符串在当前串中出现了多少次

SAM大叔的自动机~~

对于每个询问就是在后缀自动机上找到该子串所对应的节点 找不到返回0

然后这个节点的Right集合的大小就是这个子串的出现次数

每次Extend的时候将新建节点沿着parent指针到根的路径上所有点的Right集合大小+1即可

分裂节点的时候要将Right集合一并复制

这方法虽然暴力但是非常有效

由于parent是一棵树,所以可以用LCT来维护这棵树 Extend的时候直接路径修改 O(logn)

结果尼玛反倒变慢了。。。

此外就是解密过程是不改变mask的值的 mask的值只在Query的时候改变 小心被坑

暴力:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int q,mask;
char s[3003003];
namespace Suffix_Automaton{
    struct SAM{
        SAM *son[26],*parent;
        int dpt,size;
        SAM(int _=0):parent(0x0),dpt(_),size(0)
        {
            memset(son,0,sizeof son);
        }
    }*root=new SAM,*last=root;
    void Extend(int x)
    {
        SAM *p=last;
        SAM *np=new SAM(p->dpt+1);
        while(p&&!p->son[x])
            p->son[x]=np,p=p->parent;
        if(!p) np->parent=root;
        else
        {
            SAM *q=p->son[x];
            if(q->dpt==p->dpt+1)
                np->parent=q;
            else
            {
                SAM *nq=new SAM(p->dpt+1);
                memcpy(nq->son,q->son,sizeof nq->son);
                nq->parent=q->parent;
                q->parent=nq;np->parent=nq;
                nq->size=q->size;
                for(;p&&p->son[x]==q;p=p->parent)
                    p->son[x]=nq;
            }
        }
        last=np;
        for(;np;np=np->parent)
            np->size++;
    }
    void Insert()
    {
        int i;
        for(i=1;s[i];i++)
            Extend(s[i]-'A');
    }
    int Query(char *s)
    {
        SAM *p;
        for(p=root;p;p=p->son[(*s++)-'A'])
            if(!*s) return p->size;
        return 0;
    }
}
void Decode(char s[],int mask)
{
    int i,n=strlen(s);
    for(i=0;i<n;i++)
    {
        mask=(mask*131+i)%n;
        swap(s[i],s[mask]);
    }
}
int main()
{
    int i;
    char p[100];
    cin>>q;
    scanf("%s",s+1);
    Suffix_Automaton::Insert();
    for(i=1;i<=q;i++)
    {
        scanf("%s%s",p,s+1);
        Decode(s+1,mask);
        if(p[0]=='Q')
        {
            int temp=Suffix_Automaton::Query(s+1);
            mask^=temp;
            printf("%d\n",temp);
        }
        else
            Suffix_Automaton::Insert();
    }
}

LCT:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int q,mask;
char s[3003003];
namespace Link_Cut_Tree{
    struct abcd{
        abcd *fa,*ls,*rs;
        int val,add_mark;
        abcd();
        void Push_Down();
        void Add(int x);
    }*null=new abcd;
    abcd :: abcd()
    {
        fa=ls=rs=null;
        val=add_mark=0;
    }
    void abcd :: Push_Down()
    {
        if(fa->ls==this||fa->rs==this)
            fa->Push_Down();
        if(add_mark)
        {
            ls->Add(add_mark);
            rs->Add(add_mark);
            add_mark=0;
        }
    }
    void abcd :: Add(int x)
    {
        val+=x;
        add_mark+=x;
    }
    void Zig(abcd *x)
    {
        abcd *y=x->fa;
        y->ls=x->rs;
        x->rs->fa=y;
        x->rs=y;
        x->fa=y->fa;
        if(y==y->fa->ls)
            y->fa->ls=x;
        else if(y==y->fa->rs)
            y->fa->rs=x;
        y->fa=x;
    }
    void Zag(abcd *x)
    {
        abcd *y=x->fa;
        y->rs=x->ls;
        x->ls->fa=y;
        x->ls=y;
        x->fa=y->fa;
        if(y==y->fa->ls)
            y->fa->ls=x;
        else if(y==y->fa->rs)
            y->fa->rs=x;
        y->fa=x;
    }
    void Splay(abcd *x)
    {
        x->Push_Down();
        while(x->fa->ls==x||x->fa->rs==x)
        {
            abcd *y=x->fa,*z=y->fa;
            if(x==y->ls)
            {
                if(y==z->ls)
                    Zig(y);
                Zig(x);
            }
            else
            {
                if(y==z->rs)
                    Zag(y);
                Zag(x);
            }
        }
    }
    void Access(abcd *x)
    {
        abcd *y=null;
        while(x!=null)
        {
            Splay(x);
            x->rs=y;
            y=x;x=x->fa;
        }
    }
    void Cut(abcd *x)
    {
        Access(x);
        Splay(x);
        x->ls->fa=null;
        x->ls=null;
    }
    void Link(abcd *x,abcd *y)
    {
        Cut(x);
        x->fa=y;
    }

}
namespace Suffix_Automaton{
    struct SAM{
        SAM *son[26],*parent;
        Link_Cut_Tree::abcd *tree;
        int dpt;
        SAM(int _=0):parent(0x0),dpt(_)
        {
            memset(son,0,sizeof son);
            tree=new Link_Cut_Tree::abcd;
        }
    }*root=new SAM,*last=root;
    void Extend(int x)
    {
        SAM *p=last;
        SAM *np=new SAM(p->dpt+1);
        while(p&&!p->son[x])
            p->son[x]=np,p=p->parent;
        if(!p)
        {
            np->parent=root;
            Link_Cut_Tree::Link(np->tree,root->tree);
        }
        else
        {
            SAM *q=p->son[x];
            if(q->dpt==p->dpt+1)
            {
                np->parent=q;
                Link_Cut_Tree::Link(np->tree,q->tree);
            }
            else
            {
                SAM *nq=new SAM(p->dpt+1);
                memcpy(nq->son,q->son,sizeof nq->son);
                nq->parent=q->parent;
                Link_Cut_Tree::Link(nq->tree,q->parent->tree);
                q->parent=nq;np->parent=nq;
                Link_Cut_Tree::Link(q->tree,nq->tree);
                Link_Cut_Tree::Link(np->tree,nq->tree);
                //nq->size=q->size;
                q->tree->Push_Down();
                nq->tree->val=q->tree->val;
                for(;p&&p->son[x]==q;p=p->parent)
                    p->son[x]=nq;
            }
        }
        last=np;
        //for(;np;np=np->parent)
        //  np->size++;
        Link_Cut_Tree::Access(np->tree);
        Link_Cut_Tree::Splay(np->tree);
        np->tree->Add(1);
    }
    void Insert()
    {
        int i;
        for(i=1;s[i];i++)
            Extend(s[i]-'A');
    }
    int Query(char *s)
    {
        SAM *p;
        for(p=root;p;p=p->son[(*s++)-'A'])
            if(!*s) return p->tree->Push_Down(),p->tree->val;
        return 0;
    }
}
void Decode(char s[],int mask)
{
    int i,n=strlen(s);
    for(i=0;i<n;i++)
    {
        mask=(mask*131+i)%n;
        swap(s[i],s[mask]);
    }
}
int main()
{
    int i;
    char p[100];
    cin>>q;
    scanf("%s",s+1);
    Suffix_Automaton::Insert();
    for(i=1;i<=q;i++)
    {
        scanf("%s%s",p,s+1);
        Decode(s+1,mask);
        if(p[0]=='Q')
        {
            int temp=Suffix_Automaton::Query(s+1);
            mask^=temp;
            printf("%d\n",temp);
        }
        else
            Suffix_Automaton::Insert();
    }
}
时间: 2024-08-03 06:37:04

BZOJ 2555 Substring 后缀自动机+Link-Cut-Tree的相关文章

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

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

BZOJ 2555: SubString [后缀自动机 LCT]

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

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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 要维护 right 集合的大小.因为 fa 会变,且 fa 构成一棵树,所以考虑用 LCT 来维护-- 和平常写的 LCT 不太一样.因为要的值是原树上子树里的值,所以没有 makeroot ,splay 里不维护 splay 里的子树信息,只维护加法标记,表示 link 一下就给原树的自己到根的那条链上的所有点加了自己的值.cut 就是减掉自己的值.所以 query 或者 spla

BZOJ 2555 SubString 后缀自动机

题目大意:给出一个字符串,支持在线在字符串后面加一个字符串,查询一个字符串在串中出现过几次. 思路:如果不想写正解的话,这个题就是后缀自动机的简单应用.正解其实是LCT+SAM,但是时间比暴力慢一倍... 暴力就很简单了,正序建立后缀自动机,每次查询的时候找到位置直接输出size的值.注意两点,一个是分裂节点的时候,size也要复制过去.查询的时候发现找不到要return 0; CODE: #include <cstdio> #include <cstring> #include

2555: SubString 后缀自动机+LCT

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

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

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

link cut tree 入门

鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<queue> using namespace std; #define rep(i,s,

Link Cut Tree学习笔记

从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子树信息 小结 动态树问题和Link Cut Tree 动态树问题是一类要求维护一个有根树森林,支持对树的分割, 合并等操作的问题. Link Cut Tree(林可砍树?简称LCT)是解决这一类问题的一种数据结构. 一些无聊的定义 Link Cut Tree维护的是动态森林中每棵树的任意链剖分. P

Codeforces Round #339 (Div. 2) A. Link/Cut Tree

A. Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, which is based on Splay trees. Specifically, he is now studying the expose procedure. Unfortunately, Rostislav is unable to understand the definition