P6640 [BJOI2020] 封印(后缀自动机+线段树+二分)

首先对每个位置处理出它作为右端点的最长子序列zz[i]

这个问题是老题

然后,每次区间询问就是求

max(i-max(l,i-zz[i]+1)+1)

这里注意到,i-zz[i]+1这玩意好像是单调不减的?

尝试证一下...

那么可以二分区间内第一个i-zz[i]+1大于l的位置p

p前面半段的答案稳定

p后面半段的答案用线段树维护zz[i]的最大值即可

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
int len[maxn],link[maxn],nxt[maxn][26];
int sz[maxn];
int tot=1,lst=1;
string s,t;
int n;
void sam_extend (char c) {
	int cur=++tot;
	len[cur]=len[lst]+1;
	sz[cur]=1;
	int p=lst;
	while (p&&!nxt[p][c-'a']) {
		nxt[p][c-'a']=cur;
		p=link[p];
	}
	if (!p) {
		link[cur]=1;
	}
	else {
		int q=nxt[p][c-'a'];
		if (len[p]+1==len[q]) {
			link[cur]=q;
		}
		else {
			int clone=++tot;
			len[clone]=len[p]+1;
			for (int i=0;i<26;i++) {
				nxt[clone][i]=nxt[q][i];
			}
			link[clone]=link[q];
			while (p&&nxt[p][c-'a']==q) {
				nxt[p][c-'a']=clone;
				p=link[p];
			}
			link[q]=link[cur]=clone;
		}
	}
	lst=cur;
}
vector<int> g[maxn];
int z[maxn];
int zz[maxn];
int c[maxn];
void build (int i,int l,int r) {
	if (l==r) {
		c[i]=z[l];
		return;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	c[i]=max(c[i<<1],c[i<<1|1]);
}
int query (int i,int l,int r,int L,int R) {
	if (l>=L&&r<=R) return c[i];
	int mid=(l+r)>>1;
	int ans=0;
	if (L<=mid) ans=max(ans,query(i<<1,l,mid,L,R));
	if (R>mid) ans=max(ans,query(i<<1|1,mid+1,r,L,R));
	return ans;
} 
int main () {
	cin>>s>>t;
	n=s.size();
	for (char i:t) {
		sam_extend(i);
	} 
	int v=1,l=0;
	for (int i=0;i<s.size();i++) {
		while (v&&!nxt[v][s[i]-'a']) {
			v=link[v];
			l=len[v];
		} 
		if (nxt[v][s[i]-'a']) {
			v=nxt[v][s[i]-'a'];
			l++;
		}
		z[i+1]=l;
		zz[i+1]=i+1-l+1;
	}
	//for (int i=1;i<=n;i++) printf("%d ",z[i]);
	build(1,1,n);
	int q;
	cin>>q;
	while (q--) {
		int l,r;
		scanf("%d%d",&l,&r);
		int L=l,R=r,p=-1;
		while (L<=R) {
			int mid=(L+R)>>1;
			if (zz[mid]>l) {
				p=mid;
				R=mid-1;
			}
			else {
				L=mid+1;
			}
		}
		int ans;
		//printf("%d ",p);
		if (p!=-1)
			ans=max(p-l,query(1,1,n,p,r));
		else
			ans=r-l+1;
		printf("%d\n",ans);
	}
}

posted @ 2021-07-15 21:26  zlc0405  阅读(44)  评论(0编辑  收藏  举报