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

2555: SubString

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 1620  Solved: 471

Description

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

Input

第一行一个数Q表示操作个数
    
    第二行一个字符串表示初始字符串init
    
    接下来Q行,每行2个字符串Type,Str
    
    Type是ADD的话表示在后面插入字符串。
    
    Type是QUERY的话表示询问某字符串在当前字符串中出现了几次。
    
    为了体现在线操作,你需要维护一个变量mask,初始值为0
   
    
    读入串Str之后,使用这个过程将之解码成真正询问的串TrueStr。
    询问的时候,对TrueStr询问后输出一行答案Result
    然后mask = mask xor Result 
    插入的时候,将TrueStr插到当前字符串后面即可。

HINT:ADD和QUERY操作的字符串都需要解压

Output

Sample Input

2

A

QUERY B

ADD BBABBBBAAB

Sample Output

0

HINT

40 % 的数据字符串最终长度 <= 20000,询问次数<= 1000,询问总长度<= 10000

100 % 的数据字符串最终长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000

新加数据一组--2015.05.20

  注意这个mask不会在函数内改变……

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 using namespace std;
  5 const int maxn=1200010;
  6 int cnt,last;
  7 int FA[maxn],CH[maxn][27],len[maxn],rt[maxn];
  8 int fa[maxn],ch[maxn][2],add[maxn],key[maxn],flip[maxn];
  9
 10 void Add(int x,int d){
 11     if(!x)return;
 12     key[x]+=d;
 13     add[x]+=d;
 14 }
 15
 16 void Flip(int x){
 17     swap(ch[x][0],ch[x][1]);
 18     flip[x]^=1;
 19 }
 20
 21 void Push_down(int x){
 22     if(add[x]){
 23         Add(ch[x][0],add[x]);
 24         Add(ch[x][1],add[x]);
 25         add[x]=0;
 26     }
 27     if(flip[x]){
 28         Flip(ch[x][0]);
 29         Flip(ch[x][1]);
 30         flip[x]=0;
 31     }
 32 }
 33
 34 void Rotate(int x){
 35     int y=fa[x],g=fa[y],c=ch[y][1]==x;
 36     ch[y][c]=ch[x][c^1];fa[ch[y][c]]=y;
 37     ch[x][c^1]=y;fa[y]=x;fa[x]=g;
 38     if(rt[y])rt[y]=false,rt[x]=true;
 39     else ch[g][ch[g][1]==y]=x;
 40 }
 41
 42 void P(int x){
 43     if(!rt[x])P(fa[x]);
 44     Push_down(x);
 45 }
 46
 47 void Splay(int x){
 48     P(x);
 49     for(int y=fa[x];!rt[x];Rotate(x),y=fa[x])
 50         if(!rt[y])Rotate((ch[fa[y]][1]==y)==(ch[y][1]==x)?y:x);
 51 }
 52
 53 void Access(int x){
 54     int y=0;
 55     while(x){
 56         Splay(x);
 57         rt[ch[x][1]]=true;
 58         rt[ch[x][1]=y]=false;
 59         x=fa[y=x];
 60     }
 61 }
 62
 63 void Lca(int &x,int &y){
 64     Access(y);y=0;
 65     while(true){
 66         Splay(x);
 67         if(!fa[x])break;
 68         rt[ch[x][1]]=true;
 69         rt[ch[x][1]=y]=false;
 70         x=fa[y=x];
 71     }
 72 }
 73
 74 void Make_rt(int x){
 75     Access(x);
 76     Splay(x);
 77     Flip(x);
 78 }
 79
 80 void Change(int x,int y,int d){
 81     Lca(x,y);key[x]+=d;
 82     Add(y,d);Add(ch[x][1],d);
 83 }
 84
 85 void Link(int x,int y){
 86     Make_rt(x);
 87     Splay(x);
 88     fa[x]=y;
 89     Change(1,y,key[x]);
 90 }
 91
 92 void Cut(int x,int y){
 93     Make_rt(x);
 94     Splay(y);
 95     fa[ch[y][0]]=fa[y];
 96     fa[y]=0;rt[ch[y][0]]=true;
 97     ch[y][0]=0;
 98     Change(1,y,-key[x]);
 99 }
100
101
102
103 struct SAM{
104     void Init(){
105         memset(FA,0,sizeof(FA));
106         memset(CH,0,sizeof(CH));
107         memset(rt,-1,sizeof(rt));
108         last=cnt=1;
109     }
110
111     void Insert(int c){
112         int p=last,np=last=++cnt;len[np]=len[p]+1;key[np]=1;
113         while(p&&!CH[p][c])CH[p][c]=np,p=FA[p];
114         if(!p)FA[np]=1;
115         else{
116             int q=CH[p][c];
117             if(len[p]+1==len[q])
118                 FA[np]=q;
119             else{
120                 int nq=++cnt;len[nq]=len[p]+1;
121                 memcpy(CH[nq],CH[q],sizeof(CH[q]));
122                 FA[nq]=FA[q];FA[q]=FA[np]=nq;
123
124                 Link(nq,FA[nq]);Cut(q,FA[nq]);Link(q,nq);
125
126                 while(CH[p][c]==q)
127                     CH[p][c]=nq,p=FA[p];
128             }
129         }
130         Link(np,FA[np]);
131     }
132     void Extend(char *s){
133         int l=strlen(s);
134         for(int i=0;i<l;i++)
135             Insert(s[i]-‘A‘);
136     }
137
138     int Solve(char *s){
139         int l=strlen(s),p=1;
140         for(int i=0,c;i<l;i++){
141             c=s[i]-‘A‘;
142             if(!CH[p][c])return 0;
143             else p=CH[p][c];
144         }
145         Splay(p);
146         return key[p];
147     }
148 }sam;
149
150 char s[maxn];
151 char op[10];
152 int ans,mask,Q;
153
154 void Decode(char *str,int t){
155     int l=strlen(str);
156     for(int i=0;i<l;i++){
157         t=(t*131+i)%l;
158         swap(str[i],str[t]);
159     }
160 }
161
162 int main(){
163     sam.Init();
164     scanf("%d",&Q);
165     scanf("%s",s);
166     sam.Extend(s);
167     while(Q--){
168         scanf("%s",op);
169         scanf("%s",s);Decode(s,mask);
170         if(op[0]==‘A‘)
171             sam.Extend(s);
172         else{
173             ans=sam.Solve(s);
174             printf("%d\n",ans);
175             mask^=ans;
176         }
177     }
178     return 0;
179 }
时间: 2024-08-01 10:44:50

字符串(LCT,后缀自动机):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表示

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+SAM,但是时间比暴力慢一倍... 暴力就很简单了,正序建立后缀自动机,每次查询的时候找到位置直接输出size的值.注意两点,一个是分裂节点的时候,size也要复制过去.查询的时候发现找不到要return 0; CODE: #include <cstdio> #include <cstring> #include

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

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

51nod1469 淋漓字符串(后缀自动机)

题目大意: 首先,我们来定义一下淋漓尽致子串. 1.令原串为S. 2.设子串的长度为len,在原串S中出现的次数为k,令其出现的位置为p1, p2, ....pk(即这个子串在原串中[pi,pi + len - 1]中出现). 3.若k=1,则该子串不是淋漓尽致子串. 4.若存在pi,pj(i != j),使得S[pi - 1] = S[pj - 1],则该子串不是淋漓尽致子串. 5.若存在pi,pj(i != j),使得S[pi + len] = S[pj + len],则该字串不是淋漓尽致字

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 后缀自动机+Link-Cut-Tree

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

BZOJ 2555 Substring 题解

题意:给你一个字符串init,要求你支持两个操作        (1):在当前字符串的后面插入一个字符串        (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)        必须在线. 构建SAM,再用LCT维护Parent Tree.每次新加一个结点,对它的祖先的Right集合大小都加了1,(Right集合的定义见CLJ的ppt.这里不需要真的存储Right集合,存集合大小即可) 细节比较多,很容易写错.. 1 #include<cstdio> 2 #include&