ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BJOI2020 封印

2020-07-05 20:34:37  阅读:258  来源: 互联网

标签:子串 ch return leq int BJOI2020 封印 fa


封印

给出只包含小写字母 \(a,b\) 的两个字符串 \(s, t\),\(q\) 次询问,每次询问 \(s[l \dots r]\) 和 \(t\) 的最长公共子串长度。

\(n\leq 2\times 10^5\)。

题解

https://www.cnblogs.com/dysyn1314/p/13158865.html

考虑预处理s的每个子串是不是t的子串。发现对于一个左端点\(i (1\leq i\leq |s|)\),一定存在一个\(R[i]\),使得\(\forall j\in[i,R[i]]:s[i\dots j]\)都是\(t\)的子串,\(\forall k\in[R[i]+1,n]:s[i\dots k]\)都不是\(t\)的子串。也就是说,使得\(s[i\dots r]\)是\(t\)子串的\(r\),一定是从\(i\)开始的一段连续的区间,而\(R[i]\)就是其中最大的\(r\)。我们考虑把所有\(R[i]\)预处理出来。

从小到大枚举\(i\)。我们已经知道了\(s[i-1\dots R[i-1]]\)是\(t\)的子串。那么,\(s[i\dots R[i-1]]\)一定也是\(t\)的子串,也就是说,\(R[i]\geq R[i-1]\)。那么我们从\(R[i-1]+1\)开始,一位一位向后枚举,判断是否是\(t\)的子串。可以对\(t\)建一个SAM,这个“向后枚举”,就相当于在SAM上走转移边。同时,我们还要支持把前面的第\(i-1\)位删掉,这就相当于在SAM上跳父亲边。因为\(R[i]\)是单调的,所以时间复杂度\(O(|s|)\)(\(|s|,|t|\)同阶)。

预处理出\(R\)数组后,考虑回答询问。对于一个询问\(l,r\)。我们相当于要求出,\(\max_{i=l}^{r}\{\min(r,R[i])-i+1\}\)。对于\(\min(r,R[i])\),我们分类讨论:

  • 对于\(R[i]\leq r\)的\(i\),相当于询问\(\max_{l\leq i\leq r}\{R[i]-i+1\}\)。

  • 对于\(R[i]>r\)的\(i\),相当于询问\(r+\max_{l\leq i\leq r}\{-i\}+1\)。

如果把\(R[i]\)和\(r\)的关系看做一维,\(i\)和\(l,r\)的关系看做一维,那相当于是一个二维的区间最大值查询。可以考虑离线,把询问按右端点排序,这样\(R[i]\)和\(r\)的这一维就不存在了,我们只要做一维的区间最大值查询,可以用(两棵)线段树维护。

时间复杂度\(O(|s|+q\log |s|)\)。

CO int N=4e5+10,inf=1e9;
namespace SAM{
	int last=1,tot=1;
	array<int,26> ch[N];
	int fa[N],len[N];
	
	void extend(int c){
		int x=last,cur=last=++tot;
		len[cur]=len[x]+1;
		for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
		if(!x) {fa[cur]=1; return;}
		int y=ch[x][c];
		if(len[y]==len[x]+1) {fa[cur]=y; return;}
		int clone=++tot;
		ch[clone]=ch[y],fa[clone]=fa[y],len[clone]=len[x]+1;
		fa[cur]=fa[y]=clone;
		for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
	}
}

struct SEG{
	int tree[2*N];
	
	#define lc (x<<1)
	#define rc (x<<1|1)
	#define mid ((l+r)>>1)
	IN void push_up(int x){
		tree[x]=max(tree[lc],tree[rc]);
	}
	void build(int x,int l,int r,int v[N]){
		if(l==r) {tree[x]=v[l]; return;}
		build(lc,l,mid,v);
		build(rc,mid+1,r,v);
		push_up(x);
	}
	void insert(int x,int l,int r,int p,int v){
		if(l==r) {tree[x]=v; return;}
		if(p<=mid) insert(lc,l,mid,p,v);
		else insert(rc,mid+1,r,p,v);
		push_up(x);
	}
	int query(int x,int l,int r,int ql,int qr){
		if(ql<=l and r<=qr) return tree[x];
		if(qr<=mid) return query(lc,l,mid,ql,qr);
		if(ql>mid) return query(rc,mid+1,r,ql,qr);
		return max(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
	}
	#undef lc
	#undef rc
	#undef mid
}T1,T2;

char s[N],t[N];
int R[N],ans[N];
struct event {int l,r,id;} e[N];

int main(){
	scanf("%s%s",s+1,t+1);
	int n=strlen(s+1),m=strlen(t+1);
	for(int i=1;i<=m;++i) SAM::extend(t[i]-'a');
	int num=0;
	for(int i=1,x=1;i<=n;++i){
		using namespace SAM;
		R[i]=max(R[i-1],i-1);
		if(R[i]==i-1) x=1;
		for(;x!=1 and len[fa[x]]+1>R[i]-i+1;x=fa[x]);
		for(;R[i]+1<=n and ch[x][s[R[i]+1]-'a'];x=ch[x][s[R[i]+1]-'a'],++R[i]);
		e[++num]={i,R[i],0};
	}
	int q=read<int>();
	for(int i=1;i<=q;++i){
		int l=read<int>(),r=read<int>();
		e[++num]={l,r,i};
	}
	static int tmp[N];
	for(int i=1;i<=n;++i) tmp[i]=-inf;
	T1.build(1,1,n,tmp);
	for(int i=1;i<=n;++i) tmp[i]=-i;
	T2.build(1,1,n,tmp);
	sort(e+1,e+num+1,[&](CO event&a,CO event&b)->bool{
		return a.r!=b.r?a.r<b.r:a.id<b.id;
	});
	for(int i=1;i<=num;++i){
		if(!e[i].id){
			T1.insert(1,1,n,e[i].l,e[i].r-e[i].l+1);
			T2.insert(1,1,n,e[i].l,-inf);
		}
		else{
			int x=T1.query(1,1,n,e[i].l,e[i].r);
			int y=e[i].r+T2.query(1,1,n,e[i].l,e[i].r)+1;
			ans[e[i].id]=max(x,y);
		}
	}
	for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
	return 0;
}

标签:子串,ch,return,leq,int,BJOI2020,封印,fa
来源: https://www.cnblogs.com/autoint/p/13251559.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有