题意
给定长为 n n n 的字符串, m m m 次询问子串 [ a … b ] [a\dots b] [a…b] 的子串 与 [ c … d ] [c\dots d] [c…d] 这个子串的最长公共前缀的长度的最大值。 n , m ≤ 1 0 5 n,m\leq 10^5 n,m≤105。时限 2s。
题解
显然,假如能找到长为 x x x 的公共前缀,那么一定能找到比 x x x 短的公共前缀。所以首先二分 a n s ans ans。
一番特判之后可以不管子串的右端点,直接把它当作后缀来处理,把一个后缀 s u f ( i ) suf(i) suf(i) 记录为点 ( s a [ i ] , i ) (sa[i],i) (sa[i],i)。
我们知道 lcp ( a , b ) = min i = a + 1 b h [ i ] \operatorname{lcp}(a,b)=\min\limits_{i=a+1}^{b}h[i] lcp(a,b)=i=a+1minbh[i],所以满足 lcp ( c , s a [ x ] ) ≥ a n s \operatorname{lcp}(c,sa[x])\geq ans lcp(c,sa[x])≥ans 的 x x x 肯定是连续一段。二分它在 r k [ c ] rk[c] rk[c] 左边和右边分别能延伸多远(要求达到 O ( 1 ) O(1) O(1) 查询 lcp \operatorname{lcp} lcp,需要用 ST 表),可以得到 i i i 的范围。
又知道 a ≤ i ≤ b a\leq i\leq b a≤i≤b,所以每次 check 就相当于数一个矩形范围内的点数,有点则 a n s ans ans 可行。统计这个可以用主席树。
复杂度:
O
(
n
log
n
+
m
log
2
n
)
O(n\log n+m\log^2n)
O(nlogn+mlog2n)(预处理+二分套(二分套ST+主席树))(自带超大常数)
代码:
/**********
Author: WLBKR5
Problem: loj 2059, luogu P4094
Name: 字符串
Source: HEOI2016, TJOI2016
Algorithm: 后缀数组, 主席树, St 表
Date: 2020/05/28
Statue: accepted
Submission: loj.ac/submission/818921, www.luogu.com.cn/record/33956491
**********/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll getint(){
ll ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=1e5+10,L=17;
int n;
//------SA------
char s[N];
int sa[N],rk[N],tp[N],h[N];
int c[N];
bool cmp(int x,int y,int w){
return tp[x]==tp[y]&&tp[x+w]==tp[y+w];
}
void rsort(int m){
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[rk[tp[i]]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;--i)sa[c[rk[tp[i]]]--]=tp[i];
}
void get_sa(){
for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i;
int m=127;
rsort(m);
for(int k=1,p=0;p<n;m=p,k<<=1){
p=0;
for(int i=n-k+1;i<=n;i++)tp[++p]=i;
for(int i=1;i<=n;i++)
if(sa[i]>k) tp[++p]=sa[i]-k;
rsort(m);
memcpy(tp,rk,sizeof(tp));
rk[sa[1]]=p=1;
for(int i=2;i<=n;i++){
rk[sa[i]]=cmp(sa[i],sa[i-1],k)?p:++p;
}
}
}
void get_height(){
int t=0;
for(int i=1;i<=n;i++){
if(t)--t;
while(s[sa[rk[i]-1]+t]==s[i+t])++t;
h[rk[i]]=t;
}
}
//------ST------
int st[L][N];//min [i,i+2^j)
int l2[N];
void init_st(){
for(int i=1;i<=n;i++){
st[0][i]=h[i+1];
}
for(int i=1;i<L;i++){
for(int j=1;j<=n-(1<<(i-1));j++){
st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
}
}
int get_min(int l,int r){ //(l,r]
if(l==r)return n-sa[l]+1;
return min(st[l2[r-l]][l],st[l2[r-l]][r-(1<<l2[r-l])]);
}
//------chairman tree------
int ch[N*(L+1)][2],sz[N*(L+1)],rt[N],cnt=0;//(sa[i],i)
void add(int x,int val){
rt[x]=++cnt;
int u=rt[x],v=rt[x-1];
for(int i=L-1;i>=0;i--){
int t=(val>>i)&1;
sz[u]=sz[v]+1;
ch[u][t]=++cnt;
ch[u][t^1]=ch[v][t^1];
u=ch[u][t];v=ch[v][t];
}
sz[u]=sz[v]+1;
}
int query(int w,int x,int y){
w=rt[w];x=rt[x];
int ans=0;
for(int i=L-1;i>=0;i--){
int t=(y>>i)&1;
if(t)ans+=sz[ch[x][0]]-sz[ch[w][0]];
w=ch[w][t];x=ch[x][t];
}
ans+=sz[x]-sz[w];
return ans;
}
int query(int a,int b,int x,int y){
if(b<a||y<x)return 0;
return query(a-1,b,y)-query(a-1,b,x-1)>0;
}
void gett(int c,int len,int &x,int &y){
c=rk[c];
int l=1,r=c,mid=0;
while(l<=r){
mid=l+r>>1;
//cerr<<"gett1 "<<"("<<rk[c]<<") "<<len<<" "<<n<<" | "<<l<<" "<<r<<" "<<mid<<endl;
//cerr<<" "<<get_min(mid,rk[c])<<endl;
if(get_min(mid,c)>=len)x=mid,r=mid-1;
else l=mid+1;
}
l=c,r=n;
while(l<=r){
mid=l+r>>1;
//cerr<<"gett2 "<<c<<" "<<len<<" "<<n<<" "<<mid<<endl;
if(get_min(c,mid)>=len)y=mid,l=mid+1;
else r=mid-1;
}
}
bool check(int a,int b,int c,int d,int mid){
b=b-mid+1;
if(b<a)return 0;
//cerr<<"check "<<a<<" "<<b<<" "<<c<<" "<<d<<" "<<mid<<" "<<n<<endl;
int x=0,y=0;
gett(c,mid,x,y);
//cerr<<" check - p: "<<p.first<<" "<<p.second<<endl;
return query(x,y,a,b);
//cerr<<" check - q: "<<q<<endl;
}
int solve(int a,int b,int c,int d){
int l=1,r=min(b-a+1,d-c+1),mid=0,ans=0;
while(l<=r){
mid=l+r>>1;
if(check(a,b,c,d,mid))ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
void init(){
l2[0]=-1;
for(int i=1;i<=n;i++)l2[i]=l2[i>>1]+1;
get_sa();
for(int i=1;i<=n;i++)rk[sa[i]]=i;
//for(int i=1;i<=n;i++)cerr<<rk[i]<<" ";cerr<<endl;
get_height();
for(int i=1;i<=n;i++)cerr<<h[i]<<" ";cerr<<endl;
init_st();
for(int i=1;i<=n;i++)add(i,sa[i]);
}
int main(){
n=getint();
int m=getint();
scanf("%s",s+1);
init();
while(m--){
int a=getint(),b=getint(),c=getint(),d=getint();
printf("%d\n",solve(a,b,c,d));
}
return 0;
}