题目大意:
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a…b]的所有子串和s[c…d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?
思路:
考虑用后缀数组解决问题,后缀数组的求lcp即进行区间最小值,这里用[a,b]的每一个后缀去和c求lcp。
考虑如何优化上述过程,因为后缀的lcp具有可二分性,同又由于它是区间最小值,于是我们二分一个答案后将问题转化为判定性问题。
二分一个答案ans,同事我们可以将c的sa中的位置向左向右拓展,得到的区间即另外一个后缀所在的位置,我们只需要判断这个区间里面有没有我们需要的后缀即可,由于每次询问的区间都是连续的,直接用主席树维护即可。
/*=======================================
* Author : ylsoi
* Time : 2019.2.9
* Problem : bzoj4556
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj4556.in","r",stdin);
freopen("bzoj4556.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const int maxn=1e5+10;
int n,m;
char s[maxn];
int height[maxn<<1],sa[maxn<<1],rk[maxn<<1];
namespace Suffix_Array{
int sz,tax[maxn<<1],tp[maxn<<1];
void radix_sort(){
REP(i,1,sz)tax[i]=0;
REP(i,1,n)++tax[rk[i]];
REP(i,1,sz)tax[i]+=tax[i-1];
DREP(i,n,1)sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffix_sort(){
sz=26;
REP(i,1,n)rk[i]=s[i]-'a'+1,tp[i]=i;
radix_sort();
for(int w=1,p=0;w<n;w<<=1){
p=0;
REP(i,1,w)tp[++p]=n-w+i;
REP(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w;
radix_sort();
swap(rk,tp);
rk[sa[1]]=p=1;
REP(i,2,n)
if(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+w]==tp[sa[i]+w])rk[sa[i]]=p;
else rk[sa[i]]=++p;
sz=p;
if(sz==n)break;
}
int p=0;
REP(i,1,n){
if(p)--p;
int j=sa[rk[i]-1];
while(s[i+p]==s[j+p])++p;
height[rk[i]]=p;
}
}
}
struct Chairman_Tree{
#define mid ((l+r)>>1)
struct node{
int lc,rc,sum;
}t[maxn<<5];
int root[maxn],cnt;
void insert(int &o,int l,int r,int x){
int now=++cnt;
t[now]=t[o],o=now,++t[o].sum;
if(l==r)return;
if(x<=mid)insert(t[o].lc,l,mid,x);
else insert(t[o].rc,mid+1,r,x);
}
int query(int o1,int o2,int l,int r,int L,int R){
if(L<=l && r<=R)return t[o2].sum-t[o1].sum;
else{
int ret=0;
if(L<=mid)ret+=query(t[o1].lc,t[o2].lc,l,mid,L,R);
if(R>=mid+1)ret+=query(t[o1].rc,t[o2].rc,mid+1,r,L,R);
return ret;
}
}
#undef mid
}T;
int st[maxn][17],Log[maxn];
void init_rmq(){
REP(i,2,n)Log[i]=Log[i>>1]+1;
REP(i,1,n)st[i][0]=height[i];
REP(j,1,Log[n])REP(i,1,n-(1<<j)+1)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int solve(int a,int b,int c,int d){
int l=0,r=min(b-a+1,d-c+1),p=rk[c];
if(a<=c && b>=c)l=min(b-c+1,d-c+1);
while(l<r){
int mid=(l+r+1)>>1;
if(height[p]>=mid){
int L=p;
DREP(i,Log[p],0)if(L-(1<<i)>=2 && st[L-(1<<i)][i]>=mid)L-=(1<<i);
if(T.query(T.root[L-2],T.root[p-1],1,n,a,b-mid+1)){
l=mid;
continue;
}
}
if(height[p+1]>=mid){
int R=p+1;
DREP(i,Log[n-p],0)if(R+(1<<i)<=n && st[R+1][i]>=mid)R+=(1<<i);
if(T.query(T.root[p],T.root[R],1,n,a,b-mid+1)){
l=mid;
continue;
}
}
r=mid-1;
}
return l;
}
int main(){
File();
read(n),read(m);
scanf("%s",s+1);
Suffix_Array::suffix_sort();
REP(i,1,n){
T.root[i]=T.root[i-1];
T.insert(T.root[i],1,n,sa[i]);
}
init_rmq();
int a,b,c,d;
REP(i,1,m){
read(a),read(b),read(c),read(d);
printf("%d\n",solve(a,b,c,d));
}
return 0;
}