bzoj-2555 SubString

原创 2015年11月19日 22:28:58

题意:

给出一个字符串和m次操作;

每次有两种操作,"ADD"在串后面再加入一个串,"QUERY"查询询问串在整个字符串出现了多少次;

强制在线;


题解:

考虑对原串构建后缀自动机,并利用其为增量法构建的原理维护"ADD"操作;

然后因为这是一个自动机,所以它可以用来识别原串所有的后缀,当识别未完成时,得到的就是一个子串;

那么当延trans指针走了自动机上的某个状态,这个状态的right集合大小就是这个串的出现次数了;

但是right集合不能增量维护,如果直接每次在链上做修改那个复杂度是错的;

所以我们可以用一个数据结构来维护这个修改,那就是LCT了!

虽说如此,LCT不能直接用来维护子树权值的和,但是每次修改造成的影响都是对祖先那一条链上的,这个是可以维护的;

注意在复制结点的时候也要复制right域哦;

时间复杂度O(nlogn);


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 610000
#define M 3100000
#define S 26
using namespace std;
char str[M],op[10];
namespace LCT
{
	#define which(x) (ch[fa[x]][1]==x)
	int fa[N<<1],ch[N<<1][2],val[N<<1],cov[N<<1];
	bool rt[N<<1],rev[N<<1];
	void reverse(int x)
	{
		swap(ch[x][0],ch[x][1]);
		rev[x]^=1;
	}
	void Pushdown(int x)
	{
		if(rev[x])
		{
			reverse(ch[x][0]);
			reverse(ch[x][1]);
			rev[x]=0;
		}
		if(cov[x])
		{
			cov[ch[x][0]]+=cov[x];
			cov[ch[x][1]]+=cov[x];
			val[ch[x][0]]+=cov[x];
			val[ch[x][1]]+=cov[x];
			cov[x]=0;
		}
	}
	void down(int x)
	{
		if(!rt[x])	down(fa[x]);
		Pushdown(x);
	}
	void Rotate(int x)
	{
		int f=fa[x];
		bool k=which(x);
		ch[f][k]=ch[x][!k];
		ch[x][!k]=f;
		if(rt[f])	rt[f]^=rt[x]^=1;
		else		ch[fa[f]][which(f)]=x;
		fa[ch[f][k]]=f;
		fa[x]=fa[f];
		fa[f]=x;
	}
	void Splay(int x)
	{
		down(x);
		while(!rt[x])
		{
			int f=fa[x];
			if(rt[f])
			{
				Rotate(x);
				break;
			}
			if(which(x)^which(f))
				Rotate(x);
			else
				Rotate(f);
		}
	}
	void access(int x)
	{
		int y=0;
		while(x)
		{
			Splay(x);
			rt[ch[x][1]]=1;
			rt[y]=0;
			ch[x][1]=y;
			y=x,x=fa[x];
		}
	}
	void Mtr(int x)
	{
		access(x);
		Splay(x);
		reverse(x);
	}
	void update(int x,int v)
	{
		Mtr(x),access(1),Splay(x);
		val[x]+=v;cov[x]+=v;
	}
	void Link(int x,int y)
	{
		Mtr(x);
		fa[x]=y;
	}
	void Cut(int x,int y)
	{
		Mtr(x),access(y),Splay(x);
		fa[ch[x][1]]=0;
		rt[ch[x][1]]=1;
		ch[x][1]=0;
	}
	int getsum(int x)
	{
		if(!x)	return 0;
		Splay(x);
		return val[x];
	}
	#undef which
}
namespace SAM
{
	int son[N<<1][S],pre[N<<1],len[N<<1];
	bool is[N<<1];
	int tot,last;
	int newnode(int v)
	{
		tot++;
		is[tot]=v;
		LCT::rt[tot]=1;
		return tot;
	}
	void init()
	{
		tot=0;
		last=newnode(0);
	}
	void replace(int x,int y)
	{
		if(pre[x])
		{
			if(is[x])
			LCT::update(x,-1);
			LCT::Cut(x,pre[x]);
		}
		LCT::Link(x,y);
		if(is[x])
			LCT::update(x,1);
		pre[x]=y;
	}
	void Insert(int x)
	{
		int p,np=newnode(1);
		len[np]=len[last]+1;
		for(p=last;p&&!son[p][x];p=pre[p])
			son[p][x]=np;
		if(!p)
			replace(np,1);
		else
		{
			int q=son[p][x];
			if(len[q]==len[p]+1)
				replace(np,q);
			else
			{
				int nq=newnode(0);
				len[nq]=len[p]+1;
				LCT::Splay(q);
				LCT::val[nq]=LCT::val[q]-is[q];
				replace(nq,pre[q]);
				memcpy(son[nq],son[q],sizeof(int)*S);
				replace(np,nq),replace(q,nq);
				for(;son[p][x]==q;p=pre[p])
					son[p][x]=nq;
			}
		}
		last=np;
	}
	int query(char *s)
	{
		int p=1;
		while(*s!='\0')
			p=son[p][*s-'A'],s++;
		return p;
	}
}
void decode(char *s,int len,int mask)
{
	for(int j=0;j<len;j++)
	{
		mask=(mask*131+j)%len;
		swap(s[j],s[mask]);
	}
}
int main()
{
	int m,len,i,j,k,mask,ans;
	scanf("%d",&m);
	scanf("%s",str);
	len=strlen(str);
	SAM::init();
	for(i=0;i<len;i++)
		SAM::Insert(str[i]-'A');
	for(i=1,mask=0;i<=m;i++)
	{
		scanf("%s%s",op,str);
		len=strlen(str);
		decode(str,len,mask);
		if(op[0]=='A')
		{
			for(j=0;j<len;j++)
				SAM::Insert(str[j]-'A');
		}
		else
		{
			ans=LCT::getsum(SAM::query(str));
			printf("%d\n",ans);
			mask^=ans;
		}
	}
	return 0;
}



后缀自动机 + LCT 【bzoj2555】SubString

题目大意: 支持两个操作: 1、插入一串字符 2、查询一串字符在串中出现多少次。 强制在线题目分析: 维护一个动态的字符串,后缀自动机可以做。 要查询一串字符在串中出现的次数,就相当于查询...
  • Todobe
  • Todobe
  • 2017年04月12日 21:35
  • 197

[BZOJ2555][LCT][后缀自动机]SubString

题意给定一个初始字符串,要求支持两种操作,往字符串后加一个字符串和求一个字符串的出现次数。可以对字符串建后最自动机,那么一个子串x的出现次数就是|right(x)|, |right(x)|可以用fa...
  • Coldef
  • Coldef
  • 2017年02月05日 22:25
  • 105

bzoj 2555 Substring

SAM的下标我一直都是从0开始,结果带入LCT就挂了,而且蜜汁MLE,找了好久才发现,交了好多遍 /*************************************************...
  • LKC1999
  • LKC1999
  • 2016年02月15日 21:50
  • 185

[bzoj2555]substring 解题报告

考虑用splay维护sa,每次比较的时候二分+hash。注意要在两端加-∞和+∞的点。 时间复杂度O((n+q)log2n+m)O((n+q)\log^2n+m)(n是数据总长度,m是询问总长度) ...

bzoj 2555: SubString 后缀自动机+lct

WC前鏼一题求平安qaq,,,rp++         首先求出后缀自动机,然后相当于统计一个点的right集合的大小;转化为一个树动态加点和增减边的lct问题,然后就码码码就好了。 AC代码如下...

BZOJ2555: SubString

后缀自动机(Suffix Automaton) + 动态树(Link Cut Tree)
  • wy1627
  • wy1627
  • 2016年08月12日 17:43
  • 91

【BZOJ2555】SubString 后缀自动机 暴力

题解:正解是LCT+SAM。 但是出题人太神以至于不屑于卡你,所以只写个SAM然后暴力维护就好了。 这样虽然很不科学,但是可以比正解快三倍。 我是太困了吧,写挂了调了好久(发呆了好久然后秒调过,或...
  • Vmurder
  • Vmurder
  • 2015年01月20日 11:08
  • 2051

【后缀自动机】 BZOJ 2555 SubString

在当前字符串的后面插入一个字符串 询问字符串s在当前字符串中出现了几次?(作为连续子串) 必须在线支持这些操作。 只要维护后缀自动机上每一个节点的right集合大小就可以了。。。 #inclu...

【bzoj2555】SubString

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

BZOJ2555 SubString 后缀自动机+LCT

题意:给一个串,支持 1.询问一个串在当前串中的出现次数 2.在串尾加一个字符串 强制在线 Sol: 如果没有添加操作,答案就是将给定串在SAM上跑到的最终状态的Right集合大小(到不了就...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bzoj-2555 SubString
举报原因:
原因补充:

(最多只允许输入30个字)