[HEOI2016/TJOI2016]字符串
不知道更好还是更差的阅读体验
题目描述
给定长度为
数据范围:
时间限制:
Solution
对于每个子串求
有一个很显然的性质:若
因此考虑二分。二分答案的最大值,假设当前答案为
- 我们可以找到一个子串
ans 完全位于[a,b] 之间,则ans 的开头应该位于[a,b-len+1] 之间。 -
由
我们可以二分出这个区间,然后检验第一个性质是否成立,这步可以通过主席树来实现。主席树存储可能存在答案的区间内是否有
时间复杂度为
Code(实现极丑)
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100010;
char s[maxn];
int sa[maxn],c[maxn],x[maxn],y[maxn],n,m;
int rk[maxn],height[maxn];
int q,tot,st[maxn][18],lg2[maxn],root[maxn];
template<class T>inline T Min(const T &a,const T &b){return a<b?a:b;}
inline void GetSA(){
for(int i=1;i<=n;++i)++c[x[i]=s[i]];
for(int i=2;i<=m;++i)c[i]+=c[i-1];
for(int i=n;i;--i)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;++i)y[++num]=i;
for(int i=1;i<=n;++i)
if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=1;i<=m;++i)c[i]=0;
for(int i=1;i<=n;++i)++c[x[i]];
for(int i=2;i<=m;++i)c[i]+=c[i-1];
for(int i=n;i;--i)sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1,num=1;
for(int i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==m)break;
m=num;
}
}
inline void GetHeight(){
for(int i=1;i<=n;++i)rk[sa[i]]=i;
for(int i=1,k=0;i<=n;++i){
if(rk[i]==1)continue;
if(k)--k;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])++k;
height[rk[i]]=k;
}
}
struct SegmentTree{
int lc,rc,v;
}tr[maxn*20];
inline void pushup(int u){
tr[u].v=tr[tr[u].lc].v+tr[tr[u].rc].v;
}
inline int Insert(int p,int l,int r,int x){
int u=++tot;
if(l==r){tr[u].v=tr[p].v+1;return u;}
int mid=(l+r)>>1;
if(x<=mid){
tr[u].rc=tr[p].rc;
tr[u].lc=Insert(tr[p].lc,l,mid,x);
}
else{
tr[u].lc=tr[p].lc;
tr[u].rc=Insert(tr[p].rc,mid+1,r,x);
}
pushup(u);
return u;
}
inline int query(int p,int q,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return tr[q].v-tr[p].v;
int mid=(l+r)>>1,ans=0;
if(ql<=mid)ans=query(tr[p].lc,tr[q].lc,l,mid,ql,qr);
if(mid<qr)ans+=query(tr[p].rc,tr[q].rc,mid+1,r,ql,qr);
return ans;
}
inline void InitST(){
for(int i=1;i<=n;++i)st[i][0]=height[i],lg2[i]=log2(i);
for(int i=1;i<18;++i)
for(int j=1;j+(1<<i)-1<=n;++j)
st[j][i]=Min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
}
inline int queryST(int l,int r){
if(l>r)swap(l,r);
if(l==r)return n-l+1;
++l;
int t=lg2[r-l+1];
return Min(st[l][t],st[r-(1<<t)+1][t]);
}
inline bool check(int len,int a,int b,int c,int d){
int l=1,r=rk[c],ql,qr;
while(l<r){
int mid=(l+r)>>1;
if(queryST(mid,rk[c])<len)l=mid+1;
else r=mid;
}
ql=r;
l=rk[c],r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(queryST(rk[c],mid)<len)r=mid-1;
else l=mid;
}
qr=l;
if(query(root[ql-1],root[qr],1,n,a,b-len+1)>0)return true;
else return false;
}
inline int solve(int a,int b,int c,int d){
int l=0,r=Min(b-a+1,d-c+1);
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid,a,b,c,d))l=mid;
else r=mid-1;
}
return l;
}
int main(){
scanf("%d%d",&n,&q);
scanf("%s",s+1);m='z';
GetSA();GetHeight();
for(int i=1;i<=n;++i)
root[i]=Insert(root[i-1],1,n,sa[i]);
InitST();
while(q--){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
printf("%d\n",solve(a,b,c,d));
}
return 0;
}