bzoj 2555 SubString(SAM+LCT)

【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=2555

【题意】

给定一个字符串,可以随时插入字符串,提供查询s在其中作为连续子串的出现次数。

【思路】

子串的出现次数,这使我们想到了后缀自动机,如果没有插入操作,则出现次数为字符串对应节点|right|集的大小。

Right的递推方法为:|fa->right| <- |right|

因为需要不断地插入字符串,所以parent树会发生变化,我们考虑使用LCT维护parent树。SAM中插入字符串时需要改变父亲,对应到LCT中即切断原树中本来的父亲,连接新的父亲,最后还应该把np的所有fa的|right|加1,对应于LCT中的一次区间加值。

这样只需要用LCT维护一个值与一个懒标记即可。

 注意LCT原来的Link是连接两个点,而这里的Link需要切断原来的父亲后连接新父亲。

【代码】

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #define FOR(a,b,c) for(int a=b;a<=c;a++)
  5 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
  6 using namespace std;
  7
  8 const int N = 4e6+10;
  9
 10 namespace LCT {
 11
 12     struct Node {
 13         Node *ch[2],*fa;
 14         int v,add,rev;
 15         Node() ;
 16         void addv(int x) {
 17             v=v+x;
 18             add=add+x;
 19         }
 20         void reverse() {
 21             rev^=1;
 22             swap(ch[0],ch[1]);
 23         }
 24         void up_push() {
 25             if(fa->ch[0]==this||fa->ch[1]==this)
 26                 fa->up_push();
 27             if(add) {
 28                 ch[0]->addv(add);
 29                 ch[1]->addv(add);
 30                 add=0;
 31             }
 32             if(rev) {
 33                 ch[0]->reverse();
 34                 ch[1]->reverse();
 35                 rev=0;
 36             }
 37         }
 38         void maintain() {
 39         }
 40     } *null=new Node ;
 41     Node:: Node() {
 42         fa=ch[0]=ch[1]=null;
 43         add=v=rev=0;
 44     }
 45
 46     void rot(Node* o,int d) {
 47         Node *p=o->fa;
 48         p->ch[d]=o->ch[d^1];
 49         o->ch[d^1]->fa=p;
 50         o->ch[d^1]=p;
 51         o->fa=p->fa;
 52         if(p==p->fa->ch[0])
 53             p->fa->ch[0]=o;
 54         else if(p==p->fa->ch[1])
 55             p->fa->ch[1]=o;
 56         p->fa=o;
 57         p->maintain();
 58     }
 59     void splay(Node* o) {
 60         o->up_push();
 61         Node *nf,*nff;
 62         while(o->fa->ch[0]==o||o->fa->ch[1]==o) {
 63             nf=o->fa,nff=nf->fa;
 64             if(o==nf->ch[0]) {
 65                 if(nf==nff->ch[0]) rot(nf,0);
 66                 rot(o,0);
 67             } else {
 68                 if(nf==nff->ch[1]) rot(nf,1);
 69                 rot(o,1);
 70             }
 71         }
 72         o->maintain();
 73     }
 74     void Access(Node* o) {
 75         Node* son=null;
 76         while(o!=null) {
 77             splay(o);
 78             o->ch[1]=son;
 79             o->maintain();
 80             son=o; o=o->fa;
 81         }
 82     }
 83     void evert(Node* o) {
 84         Access(o);
 85         splay(o);
 86         o->reverse();
 87     }
 88     /*
 89     void Link(Node *u,Node *v) {
 90         evert(u);
 91         u->fa=v;
 92     }
 93     void Cut(Node *u,Node *v) {
 94         evert(u);
 95         Access(v); splay(v);
 96         u->fa=v->ch[0]=null;
 97         v->maintain();
 98     }
 99     */
100     void Link(Node* u,Node* v) {    //改变父亲为v 需要切断原来的父亲
101         Access(u),splay(u);
102         u->ch[0]->fa=u->ch[0]=null;
103         u->fa=v;
104     }
105
106 }
107
108 namespace SAM {
109
110     struct Snode {
111         Snode *ch[26],*fa;
112         int l;
113         LCT::Node *lct;
114         Snode(int _=0) :fa(0x0),l(_) {
115             memset(ch,0,sizeof(ch));
116             lct=new LCT::Node;
117         }
118     } *root=new Snode,*last=root;
119
120     void add(int x) {
121         Snode *p=last,*np=new Snode(p->l+1);
122         last=np;
123         for(;p&&!p->ch[x];p=p->fa)
124             p->ch[x]=np;
125         if(!p) {
126             np->fa=root;
127             LCT::Link(np->lct,root->lct);
128         } else {
129             Snode *q=p->ch[x];
130             if(q->l==p->l+1) {
131                 np->fa=q;
132                 LCT::Link(np->lct,q->lct);
133             } else {
134                 Snode* nq=new Snode(p->l+1);
135                 memcpy(nq->ch,q->ch,sizeof nq->ch);
136                 nq->fa=q->fa;
137                 LCT::Link(nq->lct,q->fa->lct);
138                 np->fa=nq; q->fa=nq;
139                 LCT::Link(q->lct,nq->lct);                    //修改parent树中的父亲
140                 LCT::Link(np->lct,nq->lct);
141                 q->lct->up_push();
142                 nq->lct->v=q->lct->v;
143
144                 for(;p&&p->ch[x]==q;p=p->fa)
145                     p->ch[x]=nq;
146             }
147         }
148         LCT::Access(np->lct);
149         LCT::splay(np->lct);
150         np->lct->addv(1);
151     }
152     void insert(char *s) {
153         for(int i=0;s[i];i++)
154             add(s[i]-‘A‘);
155     }
156     int query(char *s) {
157         for(Snode *p=root;p;p=p->ch[(*s++)-‘A‘])
158             if(!*s) return p->lct->up_push(),p->lct->v;
159         return 0;
160     }
161
162 }
163
164 void Decode(char s[],int mask)
165 {
166     int i,n=strlen(s);
167     for(int i=0;i<n;i++) {
168         mask=(mask*131+i)%n;
169         swap(s[i],s[mask]);
170     }
171 }
172
173 int q,mask;
174 char s[N],op[20];
175
176 int main()
177 {
178     scanf("%d%s",&q,s);
179     SAM::insert(s);
180     while(q--) {
181         scanf("%s%s",op,s);
182         Decode(s,mask);
183         if(op[0]==‘Q‘) {
184             int ans=SAM::query(s);
185             mask^=ans;
186             printf("%d\n",ans);
187         } else {
188             SAM::insert(s);
189         }
190     }
191     return 0;
192 }
时间: 2024-08-03 21:10:38

bzoj 2555 SubString(SAM+LCT)的相关文章

【BZOJ 2555】 2555: SubString (SAM+LCT)

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

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(后缀自动机)

http://www.lydsy.com/JudgeOnline/problem.php?id=2555 学到了如何快速维护right值orz (虽然这仍然是暴力维护,但是这是O(n)的暴力233 首先我们在加一个新节点的时候直接遍历它的parent树就行啦啦!!!!这样直接就暴力维护了...我竟然没想到.. 然后在复制新节点的时候不要忘记将right也要赋值进去.. 然后就是裸题了 #include <cstdio> #include <cstring> #include <

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

【SPOJ】Longest Common Substring(后缀自动机)

[SPOJ]Longest Common Substring(后缀自动机) 题面 Vjudge 题意:求两个串的最长公共子串 题解 \(SA\)的做法很简单 不再赘述 对于一个串构建\(SAM\) 另外一个串在\(SAM\)上不断匹配 最后计算答案就好了 匹配方法: 如果\(trans(s,c)\)存在 直接沿着\(trans\)走就行,同时\(cnt++\) 否则沿着\(parent\)往上跳 如果存在\(trans(now,c),cnt=now.longest+1\) 否则,如果不存在可行的

POJ 3693 Maximum repetition substring (后缀数组)

题目大意: 求出字典序最小,重复次数最多,的子串. 思路分析: RMQ + height 数组可以求出任意两个后缀的lcp 我们枚举答案字符串的重复的长度. 如果这个字符串的长度为 l ,而且这个字符串出现过两次或两次以上 那么你会发现在原串中  str[0] str[l] str[2*l] ....肯定有相邻的两个被包含在重复的串中. 我们求出这两个相邻的后缀的lcp 我们上面仅仅说的是被包含在重复的串中,但并不一定就是以 str[0], str[l],str[2*l]....为起点的. 那我

BZOJ 2561 最小生成树(最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2561 题意:给定一个边带正权的连通无向图G= (V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树 上? 思路:考虑克鲁斯卡尔算法的过程.若加入的 边(u,v,L)能够出现在最小生成树中,那么权值小于L的边

BZOJ 2661 连连看(费用流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2661 题意:给出一个区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x^2-y^2是一个完全平方数z^2,并且y与z互质,那么就可以将x和y一起消除,同时得到x+y点分数.要求就是,消除的数对尽可能多的前提下,得到的分数尽量多. 思路:首先暴力出所有合法的数对(x,y).然后将每个用到的数字拆成两个点,每个数对连一条边.最后的答案除以2即可. struct nod