bzoj 4556 字符串 —— 后缀数组+主席树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4556
就是找一个 rk 在一段区间内的前驱和后继;
由于 LCP 还有区间长度的限制,所以可以先二分答案!
然后直接建立 rk 的主席树,查询即可。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #define mid ((l+r)>>1) using namespace std; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } int Min(int x,int y){return x<y?x:y;} int Max(int x,int y){return x>y?x:y;} int const xn=1e5+5,xm=1e5*18; int n,m,rk[xn],tp[xn],sa[xn],tax[xn],ht[xn][20],bin[20],bit[xn]; int cnt,rt[xn],ls[xm],rs[xm],sum[xm]; char s[xn]; void rsort() { for(int i=1;i<=m;i++)tax[i]=0; for(int i=1;i<=n;i++)tax[rk[tp[i]]]++; for(int i=1;i<=m;i++)tax[i]+=tax[i-1]; for(int i=n;i;i--)sa[tax[rk[tp[i]]]--]=tp[i]; } void init() { for(int i=1;i<=n;i++)tp[i]=i,rk[i]=s[i]; m=125; rsort(); for(int k=1;k<=n;k<<=1) { int num=0; for(int i=n-k+1;i<=n;i++)tp[++num]=i; for(int i=1;i<=n;i++) if(sa[i]>k)tp[++num]=sa[i]-k; rsort(); swap(rk,tp); rk[sa[1]]=1; num=1; for(int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num; if(num==n)break; m=num; } } void geth() { int k=0; for(int i=1;i<=n;i++) { if(rk[i]==1)continue;//continue if(k)k--; int j=sa[rk[i]-1]; while(s[i+k]==s[j+k]&&i+k<=n&&j+k<=n)k++; ht[rk[i]][0]=k; } bin[0]=1; for(int i=1;i<20;i++)bin[i]=(bin[i-1]<<1); bit[1]=0; for(int i=2;i<=n;i++)bit[i]=bit[i>>1]+1; for(int i=1;i<20;i++) for(int j=1;j<=n&&j+bin[i]-1<=n;j++) ht[j][i]=Min(ht[j][i-1],ht[j+bin[i-1]][i-1]); } int getlcp(int x,int y)//rk { if(x==y)return n-sa[x]+1; if(x>y)swap(x,y); x++; int r=bit[y-x+1]; return Min(ht[x][r],ht[y-bin[r]+1][r]); } void build(int &x,int y,int l,int r,int v) { x=++cnt; ls[x]=ls[y]; rs[x]=rs[y]; sum[x]=sum[y]+1; if(l==r)return; if(v<=mid)build(ls[x],ls[y],l,mid,v); else build(rs[x],rs[y],mid+1,r,v); } int findl(int x,int y,int l,int r) { if(!(sum[x]-sum[y]))return -1; if(l==r)return l; if(sum[ls[x]]-sum[ls[y]])return findl(ls[x],ls[y],l,mid); return findl(rs[x],rs[y],mid+1,r); } int findr(int x,int y,int l,int r) { if(!(sum[x]-sum[y]))return -1; if(l==r)return l; if(sum[rs[x]]-sum[rs[y]])return findr(rs[x],rs[y],mid+1,r); return findr(ls[x],ls[y],l,mid); } int queryp(int x,int y,int l,int r,int v) { if(!(sum[x]-sum[y]))return -1; if(l==r)return l; if(v<=mid)return queryp(ls[x],ls[y],l,mid,v); else { int tmp=queryp(rs[x],rs[y],mid+1,r,v); if(tmp!=-1)return tmp; return findr(ls[x],ls[y],l,mid); } } int querys(int x,int y,int l,int r,int v) { if(!(sum[x]-sum[y]))return -1; if(l==r)return l; if(v>mid)return querys(rs[x],rs[y],mid+1,r,v); else { int tmp=querys(ls[x],ls[y],l,mid,v); if(tmp!=-1)return tmp; return findl(rs[x],rs[y],mid+1,r); } } bool ck(int a,int b,int x,int v) { int L=a,R=b-x+1; int pr=queryp(rt[R],rt[L-1],1,n,v),sc=querys(rt[R],rt[L-1],1,n,v); int len=0; if(pr!=-1)len=Max(len,getlcp(pr,v)); if(sc!=-1)len=Max(len,getlcp(sc,v)); return len>=x; } int main() { n=rd(); int Q=rd(); scanf("%s",s+1); init(); geth(); for(int i=1;i<=n;i++)build(rt[i],rt[i-1],1,n,rk[i]); for(int i=1,a,b,c,d;i<=Q;i++) { a=rd(); b=rd(); c=rd(); d=rd(); int l=0,r=Min(b-a+1,d-c+1),res; while(l<=r) { if(ck(a,b,mid,rk[c]))res=mid,l=mid+1; else r=mid-1; } printf("%d\n",res); } return 0; }