题目大意
给出只包含小写拉丁字母的字符串
S
,有
1≤|S|,m≤105
题目分析
显然可以使用后缀数组,我们要求的是
maxi∈[a,b]{min(d−c+1,b−i+1,LCP(sufi,sufc))}
根据后缀数组的性质, LCP 可以在排序后的数组上使用 RMQ 解决。
如果没有 b−i+1 的限制,我们显然选择 rank 离 rankc 尽量近的作为开头可以使 LCP 最大,对原串位置开一棵主席树,在位置 x 作为根的子树中插入
但是我们需要考虑 b−i+1 的限制,它使得 LCP 大也不一定更优。
因此我们需要设法排除对 b−i+1 取 min 的影响。考虑二分答案。我们期望答案为 mid ,那么作为开头 [b−mid+1,b] 显然都不能贡献那么多的答案,只有区间 [a,b−mid] 可能存在长度大于等于 mid 的 LCP 。那么我们就可以在这段区间查询离 rankc 最近的位置,得到最大的 LCP ,与 mid 比较一下即可。
时间复杂度 O(nlog22n) 。
最后%wyx大神使用后缀自动机怒切此题。
代码实现
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=105000;
const int LGN=17;
const int S=N*LGN;
struct chairman_tree
{
int son[S][2],size[S];
int tot;
int newnode(int cp)
{
size[++tot]=size[cp],son[tot][0]=son[cp][0],son[tot][1]=son[cp][1];
return tot;
}
void add(int &rt,int rt0,int x,int l,int r)
{
rt=newnode(rt0);
size[rt]++;
if (l==r) return;
int mid=l+r>>1;
if (x<=mid) add(son[rt][0],son[rt0][0],x,l,mid);
else add(son[rt][1],son[rt0][1],x,mid+1,r);
}
int gol(int rt,int rt0,int l,int r)
{
if (!(size[rt]-size[rt0])) return -1;
if (l==r) return l;
int mid=l+r>>1,ret;
ret=gol(son[rt][0],son[rt0][0],l,mid);
if (ret!=-1) return ret;
return gol(son[rt][1],son[rt0][1],mid+1,r);
}
int searchl(int rt,int rt0,int st,int en,int l,int r)
{
if (st>en) return -1;
if (!(size[rt]-size[rt0])) return -1;
if (st==l&&en==r) return gol(rt,rt0,l,r);
int mid=l+r>>1,ret;
if (en<=mid) return searchl(son[rt][0],son[rt0][0],st,en,l,mid);
else if (mid+1<=st) return searchl(son[rt][1],son[rt0][1],st,en,mid+1,r);
else
{
ret=searchl(son[rt][0],son[rt0][0],st,mid,l,mid);
if (ret!=-1) return ret;
return searchl(son[rt][1],son[rt0][1],mid+1,en,mid+1,r);
}
}
int gor(int rt,int rt0,int l,int r)
{
if (!(size[rt]-size[rt0])) return -1;
if (l==r) return l;
int mid=l+r>>1,ret;
ret=gor(son[rt][1],son[rt0][1],mid+1,r);
if (ret!=-1) return ret;
return gor(son[rt][0],son[rt0][0],l,mid);
}
int searchr(int rt,int rt0,int st,int en,int l,int r)
{
if (st>en) return -1;
if (!(size[rt]-size[rt0])) return -1;
if (st==l&&en==r) return gor(rt,rt0,l,r);
int mid=l+r>>1,ret;
if (en<=mid) return searchr(son[rt][0],son[rt0][0],st,en,l,mid);
else if (mid+1<=st) return searchr(son[rt][1],son[rt0][1],st,en,mid+1,r);
else
{
ret=searchr(son[rt][1],son[rt0][1],mid+1,en,mid+1,r);
if (ret!=-1) return ret;
return searchr(son[rt][0],son[rt0][0],st,mid,l,mid);
}
}
}t;
int Ws[N],Wv[N],x[N],y[N],rank[N],SA[N],height[N],root[N];
int rmq[N][LGN];
int n,m,lgn,ans;
char s[N];
bool cmp(int *r,int st1,int st2,int l){return st1+l<n&&st2+l<n&&r[st1]==r[st2]&&r[st1+l]==r[st2+l];}
void DA()
{
int i,l=1,p,mx;
for (mx=0,i=0;i<n;i++) mx=max(mx,Wv[i]=x[i]=s[i]-'a');
for (i=0;i<=mx;i++) Ws[i]=0;
for (i=0;i<n;i++) Ws[Wv[i]]++;
for (i=1;i<=mx;i++) Ws[i]+=Ws[i-1];
for (i=n-1;i>=0;i--) SA[--Ws[Wv[i]]]=i;
for (x[SA[0]]=p=0,i=1;i<n;i++) x[SA[i]]=(p+=s[SA[i]]!=s[SA[i-1]]);
for (;l<=n&&p!=n-1;l<<=1)
{
for (p=0,i=n-l;i<n;i++) y[p++]=i;
for (i=0;i<n;i++) if (SA[i]>=l) y[p++]=SA[i]-l;
for (mx=0,i=0;i<n;i++) mx=max(mx,Wv[i]=x[y[i]]);
for (i=0;i<=mx;i++) Ws[i]=0;
for (i=0;i<n;i++) Ws[Wv[i]]++;
for (i=1;i<=mx;i++) Ws[i]+=Ws[i-1];
for (i=n-1;i>=0;i--) SA[--Ws[Wv[i]]]=y[i];
for (i=0;i<n;i++) y[i]=x[i],x[i]=0;
for (x[SA[0]]=p=0,i=1;i<n;i++) x[SA[i]]=(p+=!cmp(y,SA[i-1],SA[i],l));
}
for (i=0;i<n;i++) rank[SA[i]]=i;
}
void getheight()
{
for (int i=0,j,k=0;i<n;i++,k?k--:k)
{
if (!rank[i]) continue;
j=SA[rank[i]-1];
for (;i+k<n&&j+k<n&&s[i+k]==s[j+k];) k++;
height[rank[i]]=k;
}
}
void pre()
{
lgn=trunc(log(n)/log(2));
for (int i=0;i<n;i++) rmq[i][0]=height[i];
for (int j=1;j<=lgn;j++)
for (int i=0;i+(1<<j)-1<n;i++)
if (rmq[i][j-1]<rmq[i+(1<<j-1)][j-1]) rmq[i][j]=rmq[i][j-1];
else rmq[i][j]=rmq[i+(1<<j-1)][j-1];
for (int i=0;i<n;i++)
t.add(root[i],i?root[i-1]:0,rank[i],0,n-1);
}
int getrmq(int l,int r)
{
int lgr=trunc(log(r-l+1)/log(2));
return rmq[l][lgr]<rmq[r-(1<<lgr)+1][lgr]?rmq[l][lgr]:rmq[r-(1<<lgr)+1][lgr];
}
int get(int st,int en,int x)
{
int y=t.searchr(root[en],st?root[st-1]:0,0,rank[x],0,n-1),ret=0;
if (y!=-1) ret=y!=rank[x]?getrmq(y+1,rank[x]):n-SA[y];
y=t.searchl(root[en],st?root[st-1]:0,rank[x],n-1,0,n-1);
if (y!=-1) ret=max(ret,y!=rank[x]?getrmq(rank[x]+1,y):n-SA[y]);
return ret;
}
int LCP(int st,int en,int x)
{
int l=st,r=en,mid,tmp,ret=0;
while (l<=r)
{
mid=l+r>>1;
tmp=get(st,mid,x);
if (tmp>=en-mid+1) ret=en-mid+1,r=mid-1;
else l=mid+1;
}
return ret;
}
void solve()
{
for (int i=1,a,b,c,d;i<=m;i++)
{
a=read()-1,b=read()-1,c=read()-1,d=read()-1;
ans=LCP(a,b,c);
ans=min(ans,d-c+1);
printf("%d\n",ans);
}
}
int main()
{
freopen("string.in","r",stdin),freopen("string.out","w",stdout);
n=read(),m=read();
scanf("%s",s);
DA(),getheight(),pre();
solve();
fclose(stdin),fclose(stdout);
return 0;
}