题解 LOJ3298 「BJOI2020」封印(SAM,数据结构)

题目大意

题目链接

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

数据范围:\(1\leq|s|,|t|,q\leq 2\times10^5\)

本题题解

考虑预处理\(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|)\)

参考代码(在LOJ查看):

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

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=2e5;
const int INF=1e9;

int n,m,q,R[MAXN+5];
char s[MAXN+5],t[MAXN+5];
struct SAM{
	int cnt,ed,mp[MAXN*2+5][2],fa[MAXN*2+5],len[MAXN*2+5];
	void ins(int c){
		int p=ed;ed=++cnt;len[ed]=len[p]+1;
		for(;p && !mp[p][c];p=fa[p])mp[p][c]=ed;
		if(!p)fa[ed]=1;
		else{
			int q=mp[p][c];
			if(len[q]==len[p]+1)fa[ed]=q;
			else{
				len[++cnt]=len[p]+1;
				for(int i=0;i<2;++i)mp[cnt][i]=mp[q][i];
				fa[cnt]=fa[q];fa[q]=fa[ed]=cnt;
				for(;mp[p][c]==q;p=fa[p])mp[p][c]=cnt;
			}
		}
	}
	SAM(){cnt=ed=1;}
}sam_t;

int cnt_ev,ans[MAXN+5];
struct Event{
	int l,r,id;
	Event(){}
	Event(int _l,int _r,int _id){
		l=_l;r=_r;id=_id;
	}
}ev[MAXN*2+5];
bool cmp(Event x,Event y){
	if(x.r==y.r)return x.id<y.id;
	return x.r<y.r;
}

struct SegmentTree{
	int mx[MAXN*4+5];
	void push_up(int p){
		mx[p]=max(mx[p<<1],mx[p<<1|1]);
	}
	void build(int p,int l,int r,int* arr){
		if(l==r){
			mx[p]=arr[l];
			return;
		}
		int mid=(l+r)>>1;
		build(p<<1,l,mid,arr);
		build(p<<1|1,mid+1,r,arr);
		push_up(p);
	}
	void modify(int p,int l,int r,int pos,int x){
		if(l==r){
			mx[p]=x;
			return;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)modify(p<<1,l,mid,pos,x);
		else modify(p<<1|1,mid+1,r,pos,x);
		push_up(p);
	}
	int query(int p,int l,int r,int ql,int qr){
		if(ql<=l && qr>=r)return mx[p];
		int mid=(l+r)>>1;
		int res=-INF;
		if(ql<=mid)res=query(p<<1,l,mid,ql,qr);
		if(qr>mid)res=max(res,query(p<<1|1,mid+1,r,ql,qr));
		return res;
	}
	SegmentTree(){}
}T1,T2;

int main() {
	cin>>(s+1);n=strlen(s+1);
	cin>>(t+1);m=strlen(t+1);
	for(int i=1;i<=m;++i){
		sam_t.ins(t[i]-'a');
	}
	int p=1;
	for(int i=1;i<=n;++i){
		int r=max(i-1,R[i-1]);
		if(r==i-1)p=1;
		while(p!=1 && sam_t.len[sam_t.fa[p]]+1>r-i+1)p=sam_t.fa[p];
		while(r+1<=n && sam_t.mp[p][s[r+1]-'a']!=0){
			p=sam_t.mp[p][s[r+1]-'a'];
			r++;
		}
		R[i]=r;
		ev[++cnt_ev]=Event(i,R[i],0);
		//cout<<R[i]<<" ";
	}
	//cout<<endl;
	static int tmp[MAXN+5];
	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);
	
	cin>>q;
	for(int i=1;i<=q;++i){
		int l,r;cin>>l>>r;
		ev[++cnt_ev]=Event(l,r,i);
	}
	sort(ev+1,ev+cnt_ev+1,cmp);
	for(int i=1;i<=cnt_ev;++i){
		if(ev[i].id==0){
			T1.modify(1,1,n,ev[i].l,ev[i].r-ev[i].l+1);
			T2.modify(1,1,n,ev[i].l,-INF);
		}
		else{
			int x=T1.query(1,1,n,ev[i].l,ev[i].r);
			int y=ev[i].r+T2.query(1,1,n,ev[i].l,ev[i].r)+1;
			ans[ev[i].id]=max(x,y);
		}
	}
	for(int i=1;i<=q;++i)cout<<ans[i]<<endl;
	return 0;
}
posted @ 2020-06-18 17:46  duyiblue  阅读(424)  评论(1编辑  收藏  举报