天天看點

洛谷5212 BZOJ2555 SubString SAM LCT

題目連結

題意:

給出一個字元串,有兩種操作,第一個是在目前字元串後面加上一個字元串,另一個是詢問一個字元串在目前字元串中出現了多少次。總長度差不多是 1 e 6 1e6 1e6級别的。

題意:

如果隻有詢問,求一個串在原串出現的次數的話,我們就隻需要在SAM上走到詢問串末尾在SAM上對應的點,然後查詢parent樹上子樹内的結束點的個數就可以了。假如這個時候SAM上的每一個點在parent樹上的子樹結束點個數都維護好了,那麼我們考慮加入一個結束節點的影響,其實就是找到在SAM上對應的節點之後從這個點到parent樹的根的所有點的答案都要加一。那麼我們用一個LCT來維護parent樹,實作一個鍊上加一個值和單點查詢。但是由于在動态加入字元的時候SAM的parent樹形态會改變,特别是建立節點的時候,parent樹既要link也要cut,是以隻能用LCT維護。這樣就做完了。

代碼有點長,寫的時候主要是注意在建SAM的過程中怎麼更新parent樹的形态和權值就好了,其他的都是LCT和SAM的模闆。最後說明一下,空間别開得太大,在BZOJ上直接因為申請空間比較大CE了,這種CE的編譯資訊都不會給你報錯的,當時坑了我好久,因為在洛谷上都過了,然後一直CE,還隻傳回warning不傳回error,弄得我一臉懵逼。。。

代碼:

#include <bits/stdc++.h>
using namespace std;

int n,m,len[2200010],fa[2200010],ch[2200010][26],cnt=1,rt=1,lst=1;
int f[2200010],son[2200010][2],sz[2200010],del[2200010],sta[2200010];
int mask,ans;
char s[1000010],opt[10];
inline int nroot(int x)
{
	return son[f[x]][0]==x||son[f[x]][1]==x;
}
inline void pushdown(int x)
{
	if(x&&del[x])
	{
		if(son[x][0])
		{
			sz[son[x][0]]+=del[x];
			del[son[x][0]]+=del[x];
		}
		if(son[x][1])
		{
			sz[son[x][1]]+=del[x];
			del[son[x][1]]+=del[x];
		}
		del[x]=0;
	}
}
inline void rotate(int x)
{
	int y=f[x],z=f[y],k=son[y][1]==x,w=son[x][!k];
	if(nroot(y))
	son[z][son[z][1]==y]=x;
	son[x][!k]=y;
	son[y][k]=w;
	if(w)
	f[w]=y;
	f[y]=x;
	f[x]=z;
}
inline void splay(int x)
{
	int y=x,z=0;
	sta[++z]=y;
	while(nroot(y))
	{
		y=f[y];	
		sta[++z]=y;		
	}
	while(z)
	pushdown(sta[z--]);
	while(nroot(x))
	{
		y=f[x];
		z=f[y];
		if(nroot(y))
		{
			if(son[z][1]==y^son[y][1]==x)
			rotate(x);
			else
			rotate(y);
		}
		rotate(x);
	}
}
inline void access(int x)
{
	int y=0;
	while(x!=0)
	{
		splay(x);
		son[x][1]=y;
		if(y)
		f[y]=x;
		y=x;
		x=f[x];
	}
}
inline void link(int x,int y)
{
	f[y]=x;
	access(x);
	splay(x);
	sz[x]+=sz[y];
	del[x]+=sz[y];
}
inline void cut(int x,int y)
{
	access(y);
	splay(y);
	sz[x]-=sz[y];
	del[x]-=sz[y];
	son[y][0]=0;
	f[x]=0;
}
inline void insert(int x)
{
	int cur=++cnt,pre=lst;
	lst=cur;
	len[cur]=len[pre]+1;
	for(;pre&&!ch[pre][x];pre=fa[pre])
	ch[pre][x]=cur;
	if(!pre)
	{
		fa[cur]=rt;
		f[cur]=rt;
	}	
	else
	{
		int ji=ch[pre][x];
		if(len[ji]==len[pre]+1)
		{
			fa[cur]=ji;
			f[cur]=ji;
		}		
		else
		{
			int gg=++cnt;
			len[gg]=len[pre]+1;
			memcpy(ch[gg],ch[ji],sizeof(ch[ji]));		
			fa[gg]=fa[ji];
			link(fa[ji],gg);				
			for(;pre&&ch[pre][x]==ji;pre=fa[pre])
			ch[pre][x]=gg;	
			cut(fa[ji],ji);
			fa[ji]=fa[cur]=gg;		
			link(gg,ji);
			link(gg,cur);
		}
	}
	access(cur);
	splay(cur);
	++sz[cur];
	++del[cur];
}
inline void solve(int mask)
{
	for(int i=0;i<n;++i)
	{
		mask=(mask*131+i)%n;
		char t=s[i];  
        s[i]=s[mask];  
        s[mask]=t;
	}
}
inline int sam()
{
	int cur=rt;
	for(int i=0;i<n;++i)
	{
		int x=s[i]-'A';
		cur=ch[cur][x];
	}
	if(!cur)
	return 0;
	access(cur);
	splay(cur);
	return sz[cur];
}
int main()
{
	scanf("%d",&m);
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;++i)
	{
		insert(s[i]-'A');
		s[i]=0;
	}	
	mask=0;
	for(int i=1;i<=m;++i)
	{
		scanf("%s",opt+1);
		scanf("%s",s);
		n=strlen(s);
		solve(mask);
		if(opt[1]=='A')
		{
			for(int i=0;i<n;++i)
			insert(s[i]-'A');
		}
		else
		{
			ans=sam();
			printf("%d\n",ans);
			mask^=ans;
		}
		for(int i=0;i<=n;++i)
		s[i]=0;
	}
	return 0;
}
           

繼續閱讀