Description
Solution
先用SA,把rank和sa数组弄出来,
现在二分一个mid表示答案,再二分找出在最左和最右边界L,R,使L和R为在rank中,与后缀c的最长公共前缀>=mid,再用主席树找出在这个rank区间内,是否有后缀在原来的序列中在a~(b-mid+1)之间,也就是是否有区间是合法的,
第二个二分可以用RMQ来找最长公共前缀,
复杂度:
O(nlog2(n))
;
此题要卡常!
SA用
O(nlog(n))
的,
把RMQ的log先预处理减小常数,
主席树也要注意,不要写得常数巨大,
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=100500;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans,S;
char a[N];
int er[25];
int rk[N],h[N],sa[N];
int tp[N][3],ptp[N][3];
int tpt[N];
int rmq[N][25];
struct qqww{int l,r,s;}b[N*25];
int root[N],b0;
int lg[N];
int min(int q,int w){return q<w?q:w;}
int max(int q,int w){return q>w?q:w;}
void TP()
{
fill(tpt,tpt+n+2,0);
fo(i,1,n)tpt[tp[i][2]]++;
fo(i,1,max(n,130))tpt[i]+=tpt[i-1];
fo(i,1,n)
{
int q=tpt[tp[i][2]]--;
fo(j,0,2)ptp[q][j]=tp[i][j];
}
fill(tpt,tpt+max(n,130)+2,0);
fo(i,1,n)tpt[ptp[i][1]]++;
fo(i,2,max(n,130))tpt[i]+=tpt[i-1];
fod(i,n,1)
{
int q=tpt[ptp[i][1]]--;
fo(j,0,2)tp[q][j]=ptp[i][j];
}
}
void SA()
{
er[0]=1;fo(i,1,20)er[i]=er[i-1]<<1;
fo(i,1,n)rk[i]=a[i];
int q,w,I=0;
while(er[I]<=n)
{
fo(i,1,n)tp[i][0]=i,tp[i][1]=rk[i],tp[i][2]=i+er[I]<=n?rk[i+er[I]]:0;
TP();
q=1;
fo(i,1,n)rk[tp[i][0]]=q,q+=(tp[i][1]!=tp[i+1][1]||tp[i][2]!=tp[i+1][2]);
I++;
}
fo(i,1,n)sa[rk[i]]=i;
fo(i,1,n)
{
h[i]=max(h[i-1]-1,0);
q=sa[rk[i]-1];
if(q)while(q+h[i]<=n&&a[i+h[i]]==a[q+h[i]])h[i]++;
}
fo(i,1,n)rmq[rk[i]][0]=h[i];
fo(j,1,20)fo(i,1,n)rmq[i][j]=min(rmq[i][j-1],((i+er[j-1]<=n)?rmq[i+er[j-1]][j-1]:0));
}
int RMQ(int l,int r)
{
if(l==r)return n-sa[l]+1;
int t=lg[r-l];
return min(rmq[l+1][t],rmq[r-er[t]+1][t]);
}
void build(int l,int r,int e1,int &e2,int l1)
{
e2=++b0;
if(l==r){b[e2].s=b[e1].s+1;return;}
b[e2]=b[e1];
int mid=(l+r)>>1;
if(l1<=mid)build(l,mid,b[e1].l,b[e2].l,l1);
else build(mid+1,r,b[e1].r,b[e2].r,l1);
b[e2].s=b[b[e2].l].s+b[b[e2].r].s;
}
int EF(int l,int r,int t)
{
while(l<r)
{
int mid=(l+r)>>1;
if(RMQ(mid,S)<t)l=mid+1;else r=mid;
}
return l;
}
int EF1(int l,int r,int t)
{
while(l<r)
{
int mid=((l+r)>>1)+1;
if(RMQ(S,mid)<t)r=mid-1;else l=mid;
}
return l;
}
int find(int l,int r,int e1,int e2,int l1,int r1)
{
if(!e2||0==b[e2].s-b[e1].s)return 0;
if(l==l1&&r==r1)return b[e2].s-b[e1].s;
int t=(l+r)>>1;
if(r1<=t)return find(l,t,b[e1].l,b[e2].l,l1,r1);
if(t<l1)return find(t+1,r,b[e1].r,b[e2].r,l1,r1);
else return find(l,t,b[e1].l,b[e2].l,l1,t)+find(t+1,r,b[e1].r,b[e2].r,t+1,r1);
}
int main()
{
int q,w,m_,q1,w1;
read(n);read(m_);
char ch=' ';
while(ch<'a'||ch>'z')ch=getchar();
fo(i,1,n)a[i]=ch,ch=getchar();
SA();
root[0]=0;
fo(i,1,n)build(1,n,root[i-1],root[i],sa[i]);
fo(i,1,n)lg[i]=log2(i);
while(m_--)
{
read(q),read(w),read(q1),read(w1);
S=rk[q1];
int l=0,r=min(w1-q1,w-q)+1,t,t1;
while(l<r)
{
int mid=((l+r)>>1)+1;
t=EF(1,rk[q1],mid);
t1=EF1(rk[q1],n,mid);
if(find(1,n,root[t-1],root[t1],q,w-mid+1))l=mid;
else r=mid-1;
}
ans=l;
printf("%d\n",ans);
}
return 0;
}