题意:
给定一个字符串,长度小于等于500000。让你找一个二元组序列(l[i],r[i]),使得区间[l[i+1],r[i+1]]的字符串是区间[l[i],r[i]]的子串。求最长的序列长度。
题解:
首先有一个贪心的想法,第i个字符串肯定恰好比第i+1个字符串多一个字符。
考虑对于位置i,如何判断一个答案mid是否可行,首先要满足的条件就是有一个位置j满足后缀j与后缀i的最长公共前缀>=mid-1,或者是后缀j与后缀i+1的最长公共前缀>=mid-1,这分别对应SA上的一段区间。还要满足的条件就是f[j]>=mid-1且j>i+mid。因此可以建立可持久化线段树,外层为字符串中的下标,内层为SA上的下标。每次就是查询一棵树中一段区间的最大值是否>=mid-1即可判断。
这个效率是O(n*logn*logn)。其实我们发现并不需要二分,可以证明f[i]<=f[i+1]+1,因此只需要把上限设为f[i+1]+1,然后判断,不合法就减一,再判断......
于是效率成功变成了O(nlogn)。接着我发现我忘记了后缀数组怎么写,就写了个二分hash,效率依然是两个log。
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; const int INF=1313331; int n,t[500002],h[500002],Min[22][500002],f[500002],ans,rt[500002],rk[500002],cnt,lgg[500002]; unsigned long long has[500002],cf[500002],nf; char s[500002]; typedef struct{ int Max,ls,rs; }P; P p[20000002]; bool pd(int x,int y,int l,int r){ if (x<l)return (has[y]-has[x-1])*cf[l-x]==(has[r]-has[l-1]); else return (has[y]-has[x-1])==(has[r]-has[l-1])*cf[x-l]; } bool cmp(const int& x,const int& y){ int lef=0,righ=min(n-x+1,n-y+1),mid; while(lef<righ) { mid=(lef+righ+1)/2; if (pd(x,x+mid-1,y,y+mid-1))lef=mid;else righ=mid-1; } if (lef==min(n-x+1,n-y+1))return (x>y); return s[x+lef]<s[y+lef]; } void gengxin(int r1,int r2,int begin,int end,int wz,int z){ if (begin==end) { p[r1].Max=max(z,p[r2].Max); return; } int mid=(begin+end)/2; if (wz<=mid) { p[r1].ls=++cnt;p[r1].rs=p[r2].rs; gengxin(p[r1].ls,p[r2].ls,begin,mid,wz,z); } else { p[r1].rs=++cnt;p[r1].ls=p[r2].ls; gengxin(p[r1].rs,p[r2].rs,mid+1,end,wz,z); } p[r1].Max=max(p[p[r1].ls].Max,p[p[r1].rs].Max); } int chaxun(int root,int begin,int end,int begin2,int end2){ if (begin>end2 || end<begin2)return 0; if (begin>=begin2 && end<=end2)return p[root].Max; int mid=(begin+end)/2; return max(chaxun(p[root].ls,begin,mid,begin2,end2),chaxun(p[root].rs,mid+1,end,begin2,end2)); } int minh(int x,int y){ int len=y-x+1; return min(Min[lgg[len]][x],Min[lgg[len]][y-(1<<lgg[len])+1]); } int ef1(int x,int y,int z){ int lef=x,righ=y,mid; while(lef<righ) { mid=(lef+righ)/2; if (minh(mid,y-1)>=z)righ=mid;else lef=mid+1; } return lef; } int ef2(int x,int y,int z){ int lef=x,righ=y,mid; while(lef<righ) { mid=(lef+righ+1)/2; if (minh(x,mid-1)>=z)lef=mid;else righ=mid-1; } return lef; } bool pdd(int x,int fx){ if (fx==1)return 1; int l=ef1(1,rk[x],fx-1),r=ef2(rk[x],n,fx-1); if (chaxun(rt[x+fx],1,n,l,r)>=fx-1)return 1; l=ef1(1,rk[x+1],fx-1);r=ef2(rk[x+1],n,fx-1); if (chaxun(rt[x+fx],1,n,l,r)>=fx-1)return 1; return 0; } int main() { scanf("%s",s+1);n=strlen(s+1); nf=1;cf[0]=1; for (int i=1;i<=n;i++) { has[i]=has[i-1]+s[i]*nf; nf*=INF;cf[i]=nf;t[i]=i; } sort(t+1,t+n+1,cmp); for (int i=1;i<=n;i++) for (int j=0;(1<<j)<=i;j++)lgg[i]=j; for (int i=1;i<=n;i++)rk[t[i]]=i; for (int i=1;i<n;i++) { int x=t[i],y=t[i+1]; int lef=0,righ=min(n-x+1,n-y+1),mid; while(lef<righ) { mid=(lef+righ+1)/2; if (pd(x,x+mid-1,y,y+mid-1))lef=mid;else righ=mid-1; } Min[0][i]=h[i]=lef; } for (int i=1;i<=20;i++) for (int j=1;j<=n;j++) Min[i][j]=min(Min[i-1][j],Min[i-1][j+(1<<i-1)]); for (int i=n;i>=1;i--) { f[i]=f[i+1]+1; while(!pdd(i,f[i]))f[i]--; rt[i]=++cnt; gengxin(rt[i],rt[i+1],1,n,rk[i],f[i]); ans=max(ans,f[i]); } printf("%d ",ans); return 0; }