String

Time Limit: 1000 ms Memory Limit: 1024 MB

Description

给你一个字符串S,你需要维护这个字符串S并支持两种操作:

1、在字符串S末尾插入一个字符。

2、记字符串T为字符串S从第 ll 个字符到第 rr 个字符所构成的子串。询问字符串T中最长的子串使得该子串在T中出现过至少两次(例如:T="ababa",最长的子串应为aba,长度为3),并输出它的长度。如果不存在这样的子串,则输出0。

Input

第一行为一个整数 online(0≤online≤1)online(0≤online≤1) ,在下文中用到该值。

第二行为一个字符串和一个整数m,分别表示原始的字符串(仅含小写字母)和操作个数。

记 lenlen 为字符串的长度(注意它会随时改变)。

记 lastanslastans 为上次询问的答案,初始为0。

接下来m行,每行一个操作:

若操作为"1 c"格式(c为字符),表示在字符串末尾新添一个字符。新增的字符为 (c−′a′+lastans×online) mod 26+′a′(c−′a′+lastans×online) mod 26+′a′

若操作为"2 l r"格式,表示询问。记 l′=(l−1+lastans×online) mod len+1,r′=(r−1+lastans×online) mod len+1l′=(l−1+lastans×online) mod len+1,r′=(r−1+lastans×online) mod len+1 ,真正的询问区间为 [l′,r′][l′,r′] 。我们保证 1≤l′≤r′≤len1≤l′≤r′≤len (前提是你的lastans没有错)。

Output

对于每一个询问,输出一行表示答案。

Sample Input

【样例输入1】
0
aabda 6
2 1 4
1 b
2 2 6
1 d
2 1 7
2 3 7
【样例输入2】
1
aabda 6
2 1 4
1 a
2 1 5
1 b
2 6 5
2 7 4

Sample Output

【样例输出1】
1
2
3
2
【样例输出2】
1
2
3
2

HINT

记字符串的初始长度为 nn ,操作和询问的个数之和为 qq 。

子任务1(5分): 1≤n,q≤50,online=11≤n,q≤50,online=1

子任务2(10分): 1≤n,q≤250,online=11≤n,q≤250,online=1

子任务3(15分): 1≤n,q≤1500,online=01≤n,q≤1500,online=0

子任务4(10分): 1≤n,q≤1500,online=11≤n,q≤1500,online=1

子任务5(20分): 1≤n,q≤30000,online=01≤n,q≤30000,online=0

子任务6(20分): 1≤n,q≤30000,online=11≤n,q≤30000,online=1

子任务7(20分): 1≤n,q≤50000,online=1

Solution

​ 题目比较特殊,问的是出现至少两次,这样可以少考虑一点东西。

​ 一个一个地加入字符,在加入到第(r)个字符的时候,回答([l,r])的询问。

​ 用后缀自动机来维护字符串,对每一个状态,维护一个变量(right)表示该状态在字符串最后出现位置的右端点,每新加入一个字符(str_r),设在后缀自动机上的新节点是(x),那么在parent树上,(x)到根节点1的(right)都要更新为(r)。这个操作用LCT维护parent树实现。

​ 我们发现,在更新(right)的时候,旧的(right)被赋值成了新的(r),但其实(right)(r)其实刚好代表了这个状态在字符串中最后出现的两个位置。记该状态的最长后缀长度是(len)(后缀自动机里面的(len)),那么对于(r)时间点的所有询问,用(len)更新(l)([1,right-len+1))的询问;用(right-l+1)更新(l)([right-len+1,right])的询问,由于询问的时候(-l+1)是固定的,所以用(right)更新就好了,查询的时候(-l+1)。以上两个操作用两棵线段树记录。

​ 我们发现这样更新好像忽略了很多情况,但为什么可以这样更新呢?对于同一个状态,虽然它们可能会相同地在字符串中重复很多次,但是如果我们用最右边的(right)更新,是可以覆盖到所有其他位置的更新操作的,所以用最右边的(right)来更新即可。

​ 但是这样依然不够快,有可能(x)到根节点的路径比较长,这怎么办呢?

​ 这里有个巧合:lct中,在同一条重链里面的状态,它们的(right)是相同的,因为这是维护时候自然体现的嘛。

我们发现可以用一条重链里面(len)最大的状态来代表这条链中所有的状态,按照上面说的方法更新,因为它一定能覆盖其他状态的更新。

​ 题目要求动态加入,那么把线段树可持久化。对于询问([l,r]),在(r)的时间点查询就好。

Tips

​ LCT的access操作不可调用,因为这样会破坏"同一条链的(right)相同的性质",恰好连边和删边比较好做,是树上的,特殊写一下,具体见代码,真的调了好久......

​ 对于更新操作,重写一个access操作来实现,大框架是一样的,但是注意要先断开,再更新,再连接;而不是像原版access直接换右儿子。

#include <cstdio>
#include <cstring>
using namespace std;
const int N=400005;
int online,n,_n,m,ans;
char str[N];
inline void swap(int &x,int &y){int t=x;x=y;y=t;}
inline int max(int x,int y){return x>y?x:y;}
inline int rd(){
	char c=getchar(); int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}
inline void write(int x){
	if(x>9) write(x/10);
	putchar('0'+(x%10));
}
struct SegmentTree{/*{{{*/
	int rt[N*2*19],sz,ch[N*2*19][2],maxs[N*2*19],tm[N*2*19],tmcnt;	
	void step(){tmcnt++;}
	void insert(int u1,int &u2,int l,int r,int L,int R,int val){
		if(L>R) return;
		if(!u2) u2=copy(u1);
		else if(tm[u2]!=tmcnt) u2=copy(u1);
		if(L<=l&&r<=R){
			maxs[u2]=max(maxs[u2],val);
			return;
		}
		int mid=l+r>>1;
		if(L<=mid) insert(ch[u1][0],ch[u2][0],l,mid,L,R,val);
		if(mid<R) insert(ch[u1][1],ch[u2][1],mid+1,r,L,R,val);
	}
	int query(int u,int l,int r,int pos,int ret){
		if(!u) return ret;
		ret=max(ret,maxs[u]);
		if(l==r) return ret;
		int mid=l+r>>1;
		if(pos<=mid) return query(ch[u][0],l,mid,pos,ret);
		else return query(ch[u][1],mid+1,r,pos,ret);
	}
	inline int copy(int u){
		int v=++sz;
		maxs[v]=maxs[u];  tm[v]=tmcnt;
		ch[v][0]=ch[u][0]; ch[v][1]=ch[u][1];
		return v;
	}
}seg1,seg2;/*}}}*/
struct Lct{/*{{{*/
	int rev[N*2],ch[N*2][2],fa[N*2],val[N*2],tag[N*2],len[N*2],lenmax[N*2];
	inline bool isroot(int u){return ch[fa[u]][0]!=u&&ch[fa[u]][1]!=u;}
	inline int who(int u){return ch[fa[u]][1]==u;}
	inline void update(int u){
		lenmax[u]=max(len[u],max(lenmax[ch[u][0]],lenmax[ch[u][1]]));
	}
	inline void reverse(int u){
		rev[u]^=1;
		swap(ch[u][0],ch[u][1]);
	}
	inline void maketag(int u,int x){
		tag[u]=x; val[u]=x;
	}
	inline void rotate(int u){
		int f=fa[u],g=fa[f],c=who(u);
		if(!isroot(f)) ch[g][who(f)]=u;
		fa[u]=g;
		ch[f][c]=ch[u][c^1]; 
		if(ch[f][c]) fa[ch[f][c]]=f;
		ch[u][c^1]=f; fa[f]=u;
		update(f); update(u);
	}
	inline void pushdown(int u){
		if(rev[u]){
			if(ch[u][0]) reverse(ch[u][0]);
			if(ch[u][1]) reverse(ch[u][1]);
			rev[u]=0;
		}
		if(tag[u]){
			if(ch[u][0]) maketag(ch[u][0],tag[u]);
			if(ch[u][1]) maketag(ch[u][1],tag[u]);
			tag[u]=0;
		}
	}
	inline void pd(int u){
		if(!isroot(u)) pd(fa[u]);
		pushdown(u);
	}
	inline void splay(int u){
		pd(u);
		for(;!isroot(u);rotate(u))
			if(!isroot(fa[u]))
				rotate(who(fa[u])==who(u)?fa[u]:u);
	}
	inline void link(int a,int b){
		splay(a);
		fa[a]=b;
	}
	inline void cut(int a){
		splay(a);
		if(ch[a][0]){
			fa[ch[a][0]]=fa[a]; //!!!
			ch[a][0]=0;
			update(a);
		}
		else
			fa[a]=0;
	}
	inline int query(int u){
		pd(u);
		return val[u];
	}
	void access_special(int u,int nval){
		int v;
		for(v=0;u;v=u,u=fa[u]){
			splay(u);
			ch[u][1]=0; update(u); //!!!
			int r2=val[u],maxlen=lenmax[u];
			if(r2&&maxlen){
				seg1.insert(seg1.rt[nval-1],seg1.rt[nval],1,_n+m,1,r2-maxlen,maxlen);
				seg2.insert(seg2.rt[nval-1],seg2.rt[nval],1,_n+m,r2-maxlen+1,r2,r2);
			}
			ch[u][1]=v;
			update(u);
		}
		maketag(v,nval);
	}
}lct;/*}}}*/
struct Sam{/*{{{*/
	int last,sz,len[N*2],trans[N*2][26],link[N*2];
	void init(){
		last=sz=1;
		len[1]=link[1]=0;
	}
	void expand(int c){
		int u=++sz,p=last;
		len[u]=len[p]+1; lct.len[u]=len[u];
		for(;p&&!trans[p][c];p=link[p]) trans[p][c]=u;
		if(!p) link[u]=1,lct.link(u,1);
		else{
			int q=trans[p][c];
			if(len[q]==len[p]+1) link[u]=q,lct.link(u,q);
			else{
				int v=++sz;
				len[v]=len[p]+1; lct.len[v]=len[v]; lct.val[v]=lct.query(q);
				for(int i=0;i<26;i++) trans[v][i]=trans[q][i];
				lct.cut(q); 
				lct.link(q,v); lct.link(u,v);
				lct.link(v,link[q]);
				link[v]=link[q]; 
				link[q]=link[u]=v;
				for(;trans[p][c]==q;p=link[p]) trans[p][c]=v;
			}
		}
		last=u;
	}
}sam;/*}}}*/
inline void addchar(int c){
	n++;
	sam.expand(c);
	seg1.rt[n]=seg1.rt[n-1];
	seg2.rt[n]=seg2.rt[n-1];
	seg1.step(); seg2.step();
	lct.access_special(sam.last,n);
}
inline int query(int l,int r){
	int ret=0;
	ret=max(ret,seg1.query(seg1.rt[r],1,_n+m,l,0));
	ret=max(ret,seg2.query(seg2.rt[r],1,_n+m,l,0)-l+1);
	return ret;
}
int main(){
	freopen("input.in","r",stdin);
	online=rd();
	scanf("%s",str+1); n=strlen(str+1);
	m=rd();
	_n=n; n=0;
	sam.init();
	for(int i=1;i<=_n;i++) 
		addchar(str[i]-'a');
	int opt,x,y;
	char inp[2];
	for(int i=1;i<=m;i++){
		opt=rd();
		if(opt==1){
			scanf("%s",inp);
			inp[0]=(inp[0]-'a'+ans*online)%26+'a';
			addchar(inp[0]-'a');
		}
		else{
			x=(rd()-1+ans*online)%n+1;
			y=(rd()-1+ans*online)%n+1;
			write(ans=query(x,y)); putchar('
');
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/RogerDTZ/p/8315635.html