【题目链接】

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

【题意】

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

【思路】

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

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

如果暴力做的话,可以每一次插入都重新计算right。时间复杂度为O(mn)。

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

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

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

【代码】

 #include<cstdio>
#include<cstring>
#include<iostream>
#define FOR(a,b,c) for(int a=b;a<=c;a++)
#define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
using namespace std; const int N = 4e6+; namespace LCT { struct Node {
Node *ch[],*fa;
int v,add,rev;
Node() ;
void addv(int x) {
v=v+x;
add=add+x;
}
void reverse() {
rev^=;
swap(ch[],ch[]);
}
void up_push() {
if(fa->ch[]==this||fa->ch[]==this)
fa->up_push();
if(add) {
ch[]->addv(add);
ch[]->addv(add);
add=;
}
if(rev) {
ch[]->reverse();
ch[]->reverse();
rev=;
}
}
void maintain() {
}
} *null=new Node ;
Node:: Node() {
fa=ch[]=ch[]=null;
add=v=rev=;
} void rot(Node* o,int d) {
Node *p=o->fa;
p->ch[d]=o->ch[d^];
o->ch[d^]->fa=p;
o->ch[d^]=p;
o->fa=p->fa;
if(p==p->fa->ch[])
p->fa->ch[]=o;
else if(p==p->fa->ch[])
p->fa->ch[]=o;
p->fa=o;
p->maintain();
}
void splay(Node* o) {
o->up_push();
Node *nf,*nff;
while(o->fa->ch[]==o||o->fa->ch[]==o) {
nf=o->fa,nff=nf->fa;
if(o==nf->ch[]) {
if(nf==nff->ch[]) rot(nf,);
rot(o,);
} else {
if(nf==nff->ch[]) rot(nf,);
rot(o,);
}
}
o->maintain();
}
void Access(Node* o) {
Node* son=null;
while(o!=null) {
splay(o);
o->ch[]=son;
o->maintain();
son=o; o=o->fa;
}
}
void evert(Node* o) {
Access(o);
splay(o);
o->reverse();
}
/*
void Link(Node *u,Node *v) {
evert(u);
u->fa=v;
}
void Cut(Node *u,Node *v) {
evert(u);
Access(v); splay(v);
u->fa=v->ch[0]=null;
v->maintain();
}
*/
void Link(Node* u,Node* v) { //改变父亲为v 需要切断原来的父亲
Access(u),splay(u);
u->ch[]->fa=u->ch[]=null;
u->fa=v;
} } namespace SAM { struct Snode {
Snode *ch[],*fa;
int l;
LCT::Node *lct;
Snode(int _=) :fa(0x0),l(_) {
memset(ch,,sizeof(ch));
lct=new LCT::Node;
}
} *root=new Snode,*last=root; void add(int x) {
Snode *p=last,*np=new Snode(p->l+);
last=np;
for(;p&&!p->ch[x];p=p->fa)
p->ch[x]=np;
if(!p) {
np->fa=root;
LCT::Link(np->lct,root->lct);
} else {
Snode *q=p->ch[x];
if(q->l==p->l+) {
np->fa=q;
LCT::Link(np->lct,q->lct);
} else {
Snode* nq=new Snode(p->l+);
memcpy(nq->ch,q->ch,sizeof nq->ch);
nq->fa=q->fa;
LCT::Link(nq->lct,q->fa->lct);
np->fa=nq; q->fa=nq;
LCT::Link(q->lct,nq->lct); //修改parent树中的父亲
LCT::Link(np->lct,nq->lct);
q->lct->up_push();
nq->lct->v=q->lct->v; for(;p&&p->ch[x]==q;p=p->fa)
p->ch[x]=nq;
}
}
LCT::Access(np->lct);
LCT::splay(np->lct);
np->lct->addv();
}
void insert(char *s) {
for(int i=;s[i];i++)
add(s[i]-'A');
}
int query(char *s) {
for(Snode *p=root;p;p=p->ch[(*s++)-'A'])
if(!*s) return p->lct->up_push(),p->lct->v;
return ;
} } void Decode(char s[],int mask)
{
int i,n=strlen(s);
for(int i=;i<n;i++) {
mask=(mask*+i)%n;
swap(s[i],s[mask]);
}
} int q,mask;
char s[N],op[]; int main()
{
scanf("%d%s",&q,s);
SAM::insert(s);
while(q--) {
scanf("%s%s",op,s);
Decode(s,mask);
if(op[]=='Q') {
int ans=SAM::query(s);
mask^=ans;
printf("%d\n",ans);
} else {
SAM::insert(s);
}
}
return ;
}

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

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

    2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 2548  Solved: 762 Description 懒得写背景了 ...

  2. 2019.03.01 bzoj2555: SubString(sam+lct)

    传送门 题意简述: 要求在线支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 思路: 考虑用lctlctlct来动态维护samsa ...

  3. bzoj 2555: SubString 后缀自动机+LCT

    2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 688  Solved: 235[Submit][Status][Dis ...

  4. BZOJ 2555 SubString(LCT+后缀树)

    喜闻乐见的LCT+SAM 此题要求动态插入,直接上后缀树.然后询问其实就是求一个节点的子树后缀结束节点的个数. 因为建立后缀树需要插入和删除,就直接上LCT.每次加入一个点,把它到根的路径加一 (现在 ...

  5. 【BZOJ】2555: SubString(后缀自动机)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2555 学到了如何快速维护right值orz (不过这仍然是暴力维护,可以卡到O(n) 首先我们在加一 ...

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

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 要维护 right 集合的大小.因为 fa 会变,且 fa 构成一棵树,所以考虑用 L ...

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

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 建立后缀自动机,就可以直接加入新串了: 出现次数就是 Right 集合的大小,需要查询 ...

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

    [SPOJ]Longest Common Substring(后缀自动机) 题面 Vjudge 题意:求两个串的最长公共子串 题解 \(SA\)的做法很简单 不再赘述 对于一个串构建\(SAM\) 另 ...

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

    2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1620  Solved: 471 Description 懒得写背景了 ...

随机推荐

  1. sizeof 和strlen的区别

    1. 编译时计算运算符sizeof,可用类型或变量做参数,计算占用内存的大小.sizeof后若是类型必须加括弧,若是变量名可不加括弧.sizeof(x)可用来定义数组维数.如:printf(" ...

  2. TCL语言笔记:TCL中的String命令

    一.介绍 字符串是 Tcl 中的基本数据类型,所以有大量的字符串操作命令.一个比较重要的问题就是模式匹配,通过模式匹配将字符串与指定的模式(格式)相匹配来进行字符串的比较.搜索等操作. 二.strin ...

  3. ubuntu 下搭建vsftp

    1. 安装:sudo apt-get install vsftpd 2. 我的目的是建立个ftp,专门的账户访问,账户不可以登陆.不允许匿名登陆 3. 更改配置文件/etc/vsftpd.conf l ...

  4. 在PowerDesigner中设计物理模型3——视图、存储过程和函数

    原文:在PowerDesigner中设计物理模型3--视图.存储过程和函数 视图 在SQL Server中视图定义了一个SQL查询,一个查询中可以查询一个表也可以查询多个表,在PD中定义视图与在SQL ...

  5. Linux卸载系统自带的httpd的方法

    卸载linux自带的httpd服务: 方法一: #rpm -e httpd 结果,出现以下错误 httpd-mmn = 20020628 is needed by (installed) mod_pe ...

  6. Windows下使用Visual Studio 2010编译ffmpeg全过程

    在visual studio 2010中调用ffmpeg http://blog.sina.com.cn/s/blog_4178f4bf01018wqh.html Windows下使用Visual S ...

  7. wordCount程序中MapReduce工作过程分析

    Map处理的是一个纯文本.Mapper处理的数据是由InputFormat分解过的数据集,其中InputFormat的作用是将数据集切割成小数据集InputSplit,每一个InputSplit将由一 ...

  8. MyEclipse开发WebService教程

    . 创建一个 webService 工程. 2. 创建一个普通 Java 类   3. 创建 webService 服务端 HelloJaxwsDelegate.java 的源代码如下:   4. 导 ...

  9. Android动画 三种动画

    Android可以使用三种动画 Frame Animation-帧动画 ,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果 Tween Animation-补间动画,给出两个关键帧, ...

  10. Windows下搭建MySql Master-Master Replication

    1.首先下载最新版的MySql Server (http://dev.mysql.com/downloads/windows/installer/) 2.安装MySql Server到两台机器上 My ...