bzoj-2555 SubString

题意:

给出一个字符串和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;
}



你可能感兴趣的:(后缀自动机,bzoj,LCT)