BZOJ 2555 Substring 题解

题意:给你一个字符串init,要求你支持两个操作
    
    (1):在当前字符串的后面插入一个字符串
    
    (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
    
    必须在线。

构建SAM,再用LCT维护Parent Tree。每次新加一个结点,对它的祖先的Right集合大小都加了1,(Right集合的定义见CLJ的ppt。这里不需要真的存储Right集合,存集合大小即可)

细节比较多,很容易写错。。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 const int MAXN=600000+5;
  5 const int MAXQ=3000000+5;
  6 const int SIGMA_SIZE=26;
  7 inline int idx(char c)
  8 {    return c-‘A‘;    }
  9 namespace LCT{
 10     struct Node{
 11         Node* p,*ch[2];
 12         int cnt,add;
 13         Node() {cnt=add=0;p=ch[0]=ch[1]=0;}
 14         inline bool d() const
 15         {    return p->ch[1]==this;    }
 16         inline void setc(Node* t,bool d)
 17         {
 18             ch[d]=t;
 19             t->p=this;
 20         }
 21         inline void addIt(int ad)
 22         {
 23             add+=ad;
 24             cnt+=ad;
 25         }
 26         inline void pushdown();
 27         inline bool isRoot() const;
 28     }*null;
 29     inline void Node::pushdown()
 30     {
 31         if(add)
 32         {
 33             if(ch[0]!=null) ch[0]->addIt(add);
 34             if(ch[1]!=null) ch[1]->addIt(add);
 35             add=0;
 36         }
 37     }
 38     inline bool Node::isRoot()const
 39     {    return p==null || (p->ch[0]!=this && p->ch[1]!=this);    }
 40     Node mem[MAXN<<1],*cur;
 41     inline Node* newNode()
 42     {
 43         Node* t=cur++;
 44         t->ch[0]=t->ch[1]=t->p=null;
 45         return t;
 46     }
 47     inline void rotate(Node* t)
 48     {
 49         Node* p=t->p;
 50         p->pushdown();t->pushdown();
 51         bool d=t->d();
 52         if(!p->isRoot()) p->p->setc(t,p->d());
 53         else t->p=p->p;
 54         p->setc(t->ch[d^1],d);
 55         t->setc(p,d^1);
 56     }
 57     void splay(Node* t)
 58     {
 59         t->pushdown();
 60         while(!t->isRoot())
 61         {
 62             if(t->p->isRoot()) rotate(t);
 63             else t->d()==t->p->d()?(rotate(t->p),rotate(t)):(rotate(t),rotate(t));
 64         }
 65     }
 66     inline void access(Node* u)
 67     {
 68         for(Node* v=null;u!=null;v=u,u=u->p)
 69         {
 70             splay(u);
 71             u->setc(v,1);
 72         }
 73     }
 74     void cut(Node* u) //cut u and its parent
 75     {
 76         access(u);
 77         splay(u);
 78         if(u->ch[0]==null) return;
 79         Node* f=u->ch[0];
 80         while(f->ch[1]!=null) f=f->ch[1];
 81         splay(f);
 82         f->setc(null,1);
 83         f->ch[1]->p=null;
 84     }
 85     void addToRoot(Node* u)
 86     {
 87         access(u);
 88         splay(u);
 89         u->ch[0]->addIt(1);
 90     }
 91     void init()
 92     {
 93         cur=mem;
 94         null=cur++;
 95     }
 96 }
 97
 98 struct State{
 99     State* go[SIGMA_SIZE],*suf;
100     LCT::Node *node;
101     int val;
102     State():val(0),suf(0) {memset(go,0,sizeof go);}
103 }*root,*last;
104 State stateMem[MAXN<<1],*cur;
105 State* newState()
106 {
107     cur->node=LCT::newNode();
108     return cur++;
109 }
110 void init()
111 {
112     LCT::init();
113     cur=stateMem;
114     root=last=newState();
115 }
116 void extend(int w)
117 {
118     State* p=last,*np=newState();
119     np->val=p->val+1;
120     LCT::Node* nd=np->node;
121     nd->cnt=1;
122     while(p && !p->go[w]) p->go[w]=np,p=p->suf;
123     if(!p) np->suf=root,nd->p=root->node;
124     else
125     {
126         State* q=p->go[w];
127         if(q->val==p->val+1) np->suf=q,nd->p=q->node;
128         else
129         {
130             State* nq=newState();
131             nq->val=p->val+1;
132             memcpy(nq->go,q->go,sizeof(q->go));
133             LCT::cut(q->node);
134             nq->suf=q->suf;
135             nq->node->cnt=q->node->cnt;
136             nq->node->p=q->suf->node;
137             q->suf=np->suf=nq;
138             q->node->p=nd->p=nq->node;
139             while(p && p->go[w]==q) p->go[w]=nq,p=p->suf;
140         }
141     }
142     LCT::addToRoot(nd);
143     last=np;
144 }
145 void decodeWithMask(char *s,int len,int mask)
146 {
147     for(int i=0;i<len;++i)
148     {
149         mask=(mask*131+i)%len;
150         std::swap(s[i],s[mask]);
151     }
152 }
153 int query(char* s,int len)
154 {
155 //    printf("%s\n",s);
156     State* t=root;
157     for(int i=0;i<len;++i)
158     {
159         int w=idx(s[i]);
160         if(!t->go[w]) return 0;
161         t=t->go[w];
162     }
163     LCT::splay(t->node);
164     return t->node->cnt;
165 }
166 void insert(char* s,int len)
167 {
168 //    printf("%s\n",s);
169     for(int i=0;i<len;++i)
170         extend(idx(s[i]));
171 }
172 char cmd[10],s[MAXQ];
173 int main()
174 {
175     init();
176     int Q;
177     scanf("%d",&Q);
178     scanf("%s",s);
179     insert(s,strlen(s));
180     int mask=0;
181     while(Q--)
182     {
183         scanf("%s%s",cmd,s);
184         decodeWithMask(s,strlen(s),mask);
185         if(cmd[0]==‘Q‘)
186         {
187             int t=query(s,strlen(s));
188             mask^=t;
189             printf("%d\n",t);
190         }
191         else insert(s,strlen(s));
192     }
193     return 0;
194 }

时间: 2024-08-28 13:58:04

BZOJ 2555 Substring 题解的相关文章

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表示

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

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

●BZOJ 2555 SubString

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2555题解: 后缀自动机+LCT 不难发现,对于输入的询问串,在自动机里trans后的到的状态的Right集合的大小就是答案. 那么后缀自动机本身就是支持在线添加的,问题就是如何维护好parent树,即如何维护好每个状态的Right集合. 那么Link-Cut-Tree就显然可以完成动态维护parent树的任务. (这里是维护的一颗根固定的树,没有Beroot()等换根函数) 代码: #in

bzoj 2555 SubString(SAM+LCT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2555 [题意] 给定一个字符串,可以随时插入字符串,提供查询s在其中作为连续子串的出现次数. [思路] 子串的出现次数,这使我们想到了后缀自动机,如果没有插入操作,则出现次数为字符串对应节点|right|集的大小. Right的递推方法为:|fa->right| <- |right| 因为需要不断地插入字符串,所以parent树会发生变化,我们考虑使用LCT维护parent树.SA

BZOJ 2555 SubString

题解:用LCT维护parent树的Right集合大小 为什么我的代码这么慢??? 问题:对SAM理解的还不够深 吐槽:神加密 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=1200009; int T; int fa[maxn],ch[maxn][2],tag[maxn],v[maxn],rev[maxn]; inline int son

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

题目: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

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

题目大意:给定一个初始字符串,提供两种操作: 1.在这个字符串的后面连接一个字符串 2.询问某个字符串在当前串中出现了多少次 SAM大叔的自动机~~ 对于每个询问就是在后缀自动机上找到该子串所对应的节点 找不到返回0 然后这个节点的Right集合的大小就是这个子串的出现次数 每次Extend的时候将新建节点沿着parent指针到根的路径上所有点的Right集合大小+1即可 分裂节点的时候要将Right集合一并复制 这方法虽然暴力但是非常有效 由于parent是一棵树,所以可以用LCT来维护这棵树