天天看點

[HEOI2016/TJOI2016]字元串(字尾自動機,可持久化線段樹,線段樹合并,二分答案)

[HEOI2016/TJOI2016]字元串

給出一個長度為\(n\)的字元串\(s\),和\(m\)個問題。

每個問題均有\(a,b,c,d\)四個參數,問你子串\(s[a,b]\)的所有子串和\(s[c,d]\)的最長公共字首的長度的最大值是多少?

做法:

二分一個答案\(mid\)。

現在我們要判斷\(s[c,c+mid-1]\)是否在\(s[a,b]\)出現過。

首先找到\(s[c,c+mid-1]\)所在的狀态:

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int M=maxn*40;
int len[maxn],link[maxn],nxt[maxn][26],tot=1,lst=1;
string s;
void sam_extend (char c) {
	int cur=++tot;
	len[cur]=len[lst]+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 n,m,p[maxn],father[25][maxn],T[maxn],lson[M],rson[M],c[M],tol;
int up (int u,int l,int r,int p,int v) {
	if (!u) u=++tol;
	if (l==r) {
		c[u]+=v;
		return u;
	}
	int mid=(l+r)>>1;
	if (p<=mid) lson[u]=up(lson[u],l,mid,p,v);
	if (p>mid) rson[u]=up(rson[u],mid+1,r,p,v);
	c[u]=c[lson[u]]+c[rson[u]];
	return u;
}
int merge (int x,int y,int l,int r) {
	if (!x||!y) return x+y;
	int u=++tol;
	c[u]=c[x]+c[y];
	if (l==r) return u;
	int mid=(l+r)>>1;
	lson[u]=merge(lson[x],lson[y],l,mid);
	rson[u]=merge(rson[x],rson[y],mid+1,r);
	return u;
}
int query (int u,int l,int r,int L,int R) {
	if (L>R) return 0;
	if (l>=L&&r<=R) return c[u];
	int mid=(l+r)>>1;
	int ans=0;
	if (L<=mid) ans+=query(lson[u],l,mid,L,R);
	if (R>mid) ans+=query(rson[u],mid+1,r,L,R);
	return ans;
}
void dfs (int u) {
	for (int v:g[u]) {
		father[0][v]=u;
		dfs(v);
		T[u]=merge(T[u],T[v],1,n);
	}
} 
bool check (int mid,int a,int b,int c,int d) {
	int u=p[c+mid-1];
	for (int i=20;i>=0;i--) {
		if (father[i][u]&&len[father[i][u]]>=mid) u=father[i][u];
	}
	return query(T[u],1,n,a+mid-1,b)>0;
}
int main () {
	ios::sync_with_stdio(false);
	cin>>n>>m;
	cin>>s;
	p[0]=1;
	for (int i=1;i<=n;i++) {
		sam_extend(s[i-1]);
		p[i]=lst;
		T[lst]=up(T[lst],1,n,i,1);
	} 
	for (int i=2;i<=tot;i++) g[link[i]].push_back(i);
	dfs(1);
	for (int i=1;i<=20;i++) for (int j=1;j<=tot;j++) father[i][j]=father[i-1][father[i-1][j]];
	while (m--) {
		int a,b,c,d;
		cin>>a>>b>>c>>d;
		int l=0,r=min(b-a+1,d-c+1);
		int ans=0;
		while (l<=r) {
			int mid=(l+r)>>1;
			if (check(mid,a,b,c,d)) {
				ans=mid;
				l=mid+1;
			}
			else {
				r=mid-1;
			}
		} 
		cout<<ans<<'\n';
	}
}