●BZOJ 2555 SubString

题链:

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

后缀自动机+LCT
不难发现,对于输入的询问串,在自动机里trans后的到的状态的Right集合的大小就是答案。
那么后缀自动机本身就是支持在线添加的,问题就是如何维护好parent树,即如何维护好每个状态的Right集合。
那么Link-Cut-Tree就显然可以完成动态维护parent树的任务。
(这里是维护的一颗根固定的树,没有Beroot()等换根函数)

代码:

#include<bits/stdc++.h>
#define MAXN 600005
using namespace std;
int MASK;
struct LCT{
	int size;
	int ch[MAXN*3][2],fa[MAXN*3],lazy[MAXN*3],val[MAXN*3];
	bool Who(int x){return ch[fa[x]][0]!=x;}
	bool Isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
	void Update(int x,int v){val[x]+=v; lazy[x]+=v;}
	void Pushdown(int x){
		if(!Isroot(x)) Pushdown(fa[x]);
		if(!lazy[x]) return;
		Update(ch[x][0],lazy[x]);
		Update(ch[x][1],lazy[x]);
		lazy[x]=0;
	}
	void Rotate(int x){
		static int y,z,l1,l2;
		y=fa[x]; z=fa[y];
		l1=Who(y); l2=Who(x); fa[x]=z;
		if(!Isroot(y)) ch[z][l1]=x;
		fa[y]=x; fa[ch[x][l2^1]]=y;
		ch[y][l2]=ch[x][l2^1]; ch[x][l2^1]=y;
	}
	void Splay(int x){
		static int y; Pushdown(x);
		for(;y=fa[x],!Isroot(x);Rotate(x)) if(!Isroot(y))
			Rotate(Who(y)==Who(x)?y:x);
	}
	void Access(int x){
		static int y;
		for(y=0;x;y=x,x=fa[x])
			Splay(x),ch[x][1]=y;
	}
	void Link(int x,int y){
		Access(x); Splay(x); Update(x,val[y]); fa[y]=x;
	}
	void Cut(int x){
		Access(x);
		Splay(x);
		Update(ch[x][0],-val[x]);
		fa[ch[x][0]]=0; ch[x][0]=0;
	}
	int Query(int x){
		Splay(x); return val[x];
	}
}DT;
struct SAM{
	int size,last;
	int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3];
	int Newnode(int a,int b){
		++size; maxs[size]=a;
		memcpy(trans[size],trans[b],sizeof(trans[b]));
		return size;
	}
	void Extend(int x){
		static int p,np,q,nq;
		p=last; last=np=Newnode(maxs[p]+1,0);
		for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np;
		if(!p) parent[np]=1;
		else{
			q=trans[p][x];
			if(maxs[p]+1!=maxs[q]){
				nq=Newnode(maxs[p]+1,q);
				parent[nq]=parent[q];
				parent[q]=parent[np]=nq;
				for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq;
				DT.Cut(q);
				DT.Link(parent[nq],nq);
				DT.Link(nq,q);
			}
			else parent[np]=q;
		}
		DT.val[np]=1;
		DT.Link(parent[np],np);
	}
	void Reset(){
		memset(trans[0],0,sizeof(trans[0]));
		size=0; last=Newnode(0,0);
	}
	void Add(char *S){
		for(int i=0;S[i];i++) Extend(S[i]-‘A‘);
	}
	void Query(char *T){
		static int p,ans; p=1;
		for(int i=0;T[i];i++){
			if(!trans[p][T[i]-‘A‘]){
				p=0; break;
			}
			p=trans[p][T[i]-‘A‘];
		}
		if(!p) ans=0;
		else ans=DT.Query(p);
		printf("%d\n",ans);
		MASK^=ans;
	}
}SUF;
void decode(char *S,int mask){
	static int len; len=strlen(S);
	for(int i=0;i<len;i++)
		mask=(mask*131+i)%len,swap(S[i],S[mask]);
}
int main(){
	static char S[MAXN*5],Type[10];
	int Q; scanf("%d",&Q);
	SUF.Reset();
	scanf("%s",S);
	SUF.Add(S);
	while(Q--){
		scanf("%s%s",Type,S);
		decode(S,MASK);
		if(Type[0]==‘A‘) SUF.Add(S);
		else SUF.Query(S);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8541777.html

时间: 2024-08-03 06:37:03

●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(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

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

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

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

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

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