【bzoj2555】SubString 后缀自动机+LCT

10 篇文章 0 订阅
6 篇文章 0 订阅

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

【题解】

我们把模板串建成SAM,那么一个串的出现次数就是在对应状态上的Right集的大小。

那么我们如何在线维护Right集的大小呢?可以用LCT/平衡树+dfs序

反正我用的是LCT,毕竟好写。

我们用LCT维护parent树,对于新加入的点,连一条通向parent的边即可。

注意:如果在LCT中使用了reverse操作,即把某点定为了原树的根,那么在回答询问时要重新将1结点定为根。

我的LCT代码中省去了reverse操作。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 1200010
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
int Q,n,cnt(1),now(1),mask,ans,f[MAXN],vis[MAXN],delt[MAXN],size[MAXN],mx[MAXN],stack[MAXN],pre[MAXN],son[MAXN][2],c[MAXN][2];
char ch[MAXN],s[MAXN];
string chars;
bool get(int x){return son[f[x]][1]==x;}
bool isroot(int x){return son[f[x]][0]!=x&&son[f[x]][1]!=x;}
void Tsize(int x,int v){if(!x) return; delt[x]+=v; size[x]+=v;}
void pushdown(int x){
	if(delt[x]){
		Tsize(son[x][0],delt[x]); Tsize(son[x][1],delt[x]);
		delt[x]=0;
	}
}
void rotate(int x){
	int y=f[x],z=f[y],which=get(x);
	if(!isroot(y))  son[z][son[z][1]==y]=x;
	son[y][which]=son[x][which^1]; f[son[y][which]]=y;
	son[x][which^1]=y;  f[y]=x;  f[x]=z;
	son[0][0]=son[0][1]=f[0]=size[0]=0;
}
void splay(int x){
	int top(0); stack[++top]=x;
	for(int i=x;!isroot(i);i=f[i]) stack[++top]=f[i];
	dn(i,top,1)  pushdown(stack[i]);
	for(int y=f[x];!isroot(x);rotate(x),y=f[x])
		if(!isroot(y))  rotate(get(x)==get(y)?y:x);
}
void access(int x){for(int t(0);x;t=x,x=f[x])splay(x),son[x][1]=t;}
void linkk(int x,int y){f[x]=y;access(y);splay(y);Tsize(y,size[x]);}
void cut(int x){access(x);splay(x);Tsize(son[x][0],-size[x]);f[son[x][0]]=0;son[x][0]=0;}
void insert(int x){
	int p=now,np=++cnt;
	mx[np]=mx[now]+1; now=np;  size[np]=1;
	while(!c[p][x]&&p)  c[p][x]=np,p=pre[p];
	if(!p)  pre[np]=1,linkk(np,1);
	else{
		int q=c[p][x];
		if(mx[q]==mx[p]+1)  pre[np]=q,linkk(np,q);
		else{
			int nq=++cnt;
			mx[nq]=mx[p]+1;
			memcpy(c[nq],c[q],sizeof(c[q]));
			pre[nq]=pre[q]; linkk(nq,pre[q]);
			pre[np]=pre[q]=nq; cut(q); linkk(q,nq); linkk(np,nq);
			while(c[p][x]==q&&p) c[p][x]=nq,p=pre[p];
		}
	}
}
void Load(int mask){
    scanf("%s",s);
    chars=s;
    up(i,0,chars.length()-1){
        mask=(mask*131+i)%chars.length();
            char t=chars[i];
            chars[i]=chars[mask];
            chars[mask]=t;
    }
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	scanf("%d",&Q);  scanf("%s",ch);
	n=strlen(ch);
	up(i,0,n-1)  insert(ch[i]-'A');
	up(i,1,Q){
		char opt[10];  scanf("%s",opt);  Load(mask);
		if(opt[0]=='A') up(j,0,chars.length()-1) 
			insert(chars[j]-'A');
		else{
			int x=1;
			up(j,0,chars.length()-1){
				if(c[x][chars[j]-'A']) x=c[x][chars[j]-'A'];
				else{x=0;break;}
			}
			if(!x) ans=0;
			else splay(x),ans=size[x];
			mask^=ans;
			printf("%d\n",ans);
		}
	}
	return 0;
}


附上makedata程序,供对拍使用:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 1200010
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
char ch[2]={'A','B'};
string s[2]={"QUERY","ADD"};
int main(){
	freopen(FILE".in","w",stdout);
	srand(time(NULL));
	int Q=10,n=5,m=5;
	printf("%d\n",Q);
	up(i,1,n)  printf("%c",ch[rand()%2]);
	printf("\n");
	up(i,1,Q){
		int t=rand()%2;
		if(t==0){
			cout<<s[t]<<' ';  int len=rand()%m+1;
			up(j,1,len)  printf("%c",ch[rand()%2]);
		}
		else{
			cout<<s[t]<<' ';  int len=rand()%m+1;
			up(j,1,len)  printf("%c",ch[rand()%2]);
		}
		printf("\n");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值