传送门:bzoj4556
题解
由 L C P LCP LCP自然地联想到SA。
首先二分枚举答案 m i d mid mid,在 s a sa sa数组上必然是连续的一段满足于 r k c rk_c rkc的 l c p ≥ m i d lcp\geq mid lcp≥mid,假设这段区间为 [ L , R ] [L,R] [L,R](可以二分找出/或者直接倍增)。
判断是否答案存在就是 [ L , R ] [L,R] [L,R]中是否存在 r k a , r k a + 1 , . . , r k b − m i d + 1 rk_{a},rk_{a+1},..,rk_{b-mid+1} rka,rka+1,..,rkb−mid+1中的任意一个。
那么考虑主席树按 s a i sa_i sai逐个加入即可判断。
复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
代码
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=1e5+10;
int n,m,rk[N],bin[30],lg[N],ans;
int sa[N],t1[N],t2[N],c[N];
int rt[N],ss[N*25],ls[N*25],rs[N*25],cnt;
int d[20][N];char s[N];
inline void build()
{
int i,k,p,m=26,*x=t1,*y=t2;
for(i=1;i<=n;++i) c[(x[i]=s[i]-'a'+1)]++;
for(i=2;i<=m;++i) c[i]+=c[i-1];
for(i=n;i;--i) sa[c[x[i]]--]=i;
for(k=1;k<n;k<<=1){
for(p=0,i=n-k+1;i<=n;++i) y[++p]=i;
for(i=1;i<=n;++i) if(sa[i]>k) y[++p]=sa[i]-k;
for(i=1;i<=m;++i) c[i]=0;
for(i=1;i<=n;++i) c[x[y[i]]]++;
for(i=2;i<=m;++i) c[i]+=c[i-1];
for(i=n;i;--i) sa[c[x[y[i]]]--]=y[i];
p=1;swap(x,y);x[sa[1]]=1;
for(i=2;i<=n;++i){
p+=((y[sa[i]]!=y[sa[i-1]])||(y[sa[i]+k]!=y[sa[i-1]+k]));
x[sa[i]]=p;
}
if(p>=n) break;
m=p;
}
for(i=1;i<=n;++i) rk[sa[i]]=i;
}
inline void cal()
{
int i,j,pr=0;
for(i=1;i<=n;++i){
if(rk[i]==1) {pr=0;continue;}
for(pr=max(0,pr-1),j=sa[rk[i]-1];i+pr<=n && j+pr<=n && s[i+pr]==s[j+pr];++pr);
d[0][rk[i]]=pr;
}
for(i=1;bin[i]<=n;++i)
for(j=1;j+bin[i]-1<=n;++j)
d[i][j]=min(d[i-1][j],d[i-1][j+bin[i-1]]);
}
inline int lcp(int x,int y)
{
x++;int bs=lg[y-x+1];
return min(d[bs][x],d[bs][y-bin[bs]+1]);
}
void ins(int pre,int &k,int l,int r,int pos)
{
if(k==pre){k=++cnt;ls[k]=ls[pre];rs[k]=rs[pre];ss[k]=ss[pre];}
ss[k]++;if(l^r){
if(pos<=mid) ins(ls[pre],ls[k],l,mid,pos);
else ins(rs[pre],rs[k],mid+1,r,pos);
}
}
int ask(int pre,int k,int l,int r,int L,int R)
{
if((!k)||(ss[k]==ss[pre])) return 0;
if(L<=l && r<=R) return ss[k]-ss[pre];
if(L<=mid && ask(ls[pre],ls[k],l,mid,L,R)) return 1;
if(R>mid) return ask(rs[pre],rs[k],mid+1,r,L,R);
return 0;
}
inline int gt(int x,int op,int lim)
{
int l,r,re=x;
if(!op){
l=1;r=x-1;
for(;l<=r;) lcp(mid,x)>=lim?r=(re=mid)-1:l=mid+1;
}else{
l=x+1;r=n;
for(;l<=r;) lcp(x,mid)>=lim?l=(re=mid)+1:r=mid-1;
}
return re;
}
void sol()
{
int a,b,c,d,l=1,r,L,R,x;ans=0;
scanf("%d%d%d%d",&a,&b,&c,&d);
r=min(d-c,b-a)+1;c=rk[c];
for(;l<=r;){
x=mid;L=gt(c,0,x);R=gt(c,1,x);
if(ask(rt[L-1],rt[R],1,n,a,b-x+1)) l=(ans=x)+1;
else r=x-1;
}
printf("%d\n",ans);
}
int main(){
int i,j;bin[0]=1;
scanf("%d%d%s",&n,&m,s+1);
for(i=1;i<30;++i) bin[i]=bin[i-1]<<1;
for(i=2;i<N;++i) lg[i]=lg[i>>1]+1;
build();cal();
for(i=1;i<=n;++i) rt[i]=rt[i-1],ins(rt[i-1],rt[i],1,n,sa[i]);
for(;m;--m) sol();
return 0;
}