BZOJ4556: [Tjoi2016&Heoi2016]字符串

来源:互联网 发布:数控编程图纸大全 编辑:程序博客网 时间:2024/04/30 00:45

题目大意:给一个串和多组询问,每次询问S[a....b]的所有字串和S[c...d]这个字符串的最长公共前缀是多少

 

最长公共前缀啊,大概直接想到后缀数组二分RMQ之类的了

但是现在有一个问题比较恶心,就是“S[a....b]的所有字串“和“从a....b开始的所有字串”还不太一样,因为有结束位置的限制,比如说从a位置开始的最长公共前缀是2,而从b开始的很长,那在算的时候肯定会选择b,然而b的有效匹配长度是1,这样就出现了很不好的问题....

那就只好先二分枚举长度了

这样可以直接pass掉a...b中后面一部分开始的字串,然后前面的还没有限制了

 

问题就转化为了每次询问字符串中从a...B开头的字串和c开头的字串最长公共前缀有多长?

 

这就是个很经典的问题了

首先我们先要搞个后缀数组

然后在前后各做一次二分,求出从Rank[c]开始扩展,在保证“这段区间height数组最小值”大于等于“最开始二分的长度”的情况下,最远能向两边扩展到哪

这就得到了一个区间,我们只需要查这个区间内有没有符合SA[i]∈[a,B]的即可,这步可以用主席树来做

如果有,说明这个二分的长度可以达到

如果没有,那就说明不可以

时间复杂度O(NlogN^2)

 

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 200010using namespace std;int sa[N],wa[N],wb[N],c[N];int n,m;char s[N];void buildsa(){int i,k,p,*x=wa,*y=wb;for(i=0;i<m;i++) c[i]=0;for(i=0;i<n;i++) c[x[i]=s[i]]++;for(i=1;i<m;i++) c[i]+=c[i-1];for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;for(k=1;k<=n;k*=2){p=0;for(i=n-k;i<n;i++) y[p++]=i;for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;for(i=0;i<m;i++) c[i]=0;for(i=0;i<n;i++) c[x[i]]++;for(i=1;i<m;i++) c[i]+=c[i-1];for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];p=1;swap(x,y);x[sa[0]]=0;for(i=1;i<n;i++){if(y[sa[i]]==y[sa[i-1]]&&((sa[i]+k>=n&&sa[i-1]+k>=n)||(sa[i]+k<n&&sa[i-1]+k<n&&y[sa[i]+k]==y[sa[i-1]+k])))x[sa[i]]=p-1;else x[sa[i]]=p,p++;}if(p>=n) break;m=p;}}int rnk[N],h[N];void geth(){int i,j,k=0;for(i=0;i<n;i++) rnk[sa[i]]=i;for(i=0;i<n;i++){k=max(k-1,0);if(rnk[i]==0) continue;while(s[i+k]==s[sa[rnk[i]-1]+k]) k++;h[rnk[i]]=k;}}int lo[N];int rmq[N][21];int RMQ(int x,int y){int l=lo[y-x+1];return min(rmq[x][l],rmq[y-(1<<l)+1][l]);}int rt[N],ch[N*20][2],sum[N*20],cn;void pup(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]];}void addnew(int x,int &y,int l,int r,int v,int w){cn++;y=cn;if(l==r){sum[y]=sum[x]+w;return;}ch[y][0]=ch[x][0];ch[y][1]=ch[x][1];int mid=(l+r)>>1;if(v<=mid) addnew(ch[x][0],ch[y][0],l,mid,v,w);else addnew(ch[x][1],ch[y][1],mid+1,r,v,w);pup(y);}int check(int x,int y,int L,int R,int l,int r){if(L==l&&R==r) return sum[y]-sum[x];int mid=(L+R)>>1;if(r<=mid) return check(ch[x][0],ch[y][0],L,mid,l,r);else if(l>mid) return check(ch[x][1],ch[y][1],mid+1,R,l,r);else return check(ch[x][0],ch[y][0],L,mid,l,mid)+check(ch[x][1],ch[y][1],mid+1,R,mid+1,r);}bool checkit(int k,int p,int q,int x,int y){int L,R,mid,l,r;int FG=rnk[x];if(h[FG]<k) L=FG;else{l=1;r=FG;while(l<r){mid=(l+r)>>1;if(RMQ(mid,FG)<k) l=mid+1;else r=mid;}L=l-1;}if(FG==n-1||h[FG+1]<k) R=FG;else{l=FG+1;r=n-1;while(l<r){mid=(l+r)>>1;if(RMQ(FG+1,mid)<k) r=mid;else l=mid+1;}R=l-1;}if(check(rt[L],rt[R+1],0,n,p,q)>0) return true;return false;}int main(){int Q;scanf("%d%d",&n,&Q);m=255;scanf("%s",s);int i,j,x,y,p,q;buildsa();geth();for(i=1;i<=n;i++)lo[i]=(int)(log2(i));for(i=0;i<n;i++)rmq[i][0]=h[i];for(j=1;j<=20;j++)for(i=0;i+(1<<(j-1))<n;i++)rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);int l,r,mid,L,R;for(i=1;i<=n;i++)addnew(rt[i-1],rt[i],0,n,sa[i-1],1);int Z,Y,M;while(Q--){scanf("%d%d%d%d",&p,&q,&x,&y);x--;y--;p--;q--;Z=0;Y=min(y-x+1,q-p+1)+1;while(Z<Y){M=(Z+Y)>>1;if(checkit(M,p,q-M+1,x,y)) Z=M+1;else Y=M;}printf("%d\n",Z-1);}}


 

0 0