求满足下列要求的最长子串:
1.长度不小于5
2.出现两次(子串整体加上某个值也算出现一次
3.两次出现无重叠
难处理的是第二个要求:整体增加某个数也算出现一次
稍微思考一下便会得出:整体加上某个值后,相邻数的差值是不变的
所以用一个数组r[i]保存mus[i+1]与mus[i]的差值
这里要注意将差值加上88,使差值始终为正以免在计算sa、rank的时候RE,同时字符集大小也变成了88*2
二分答案的同时将后缀按大于mid height分组,满足要求的子串肯定是同一组内的后缀的公共前缀
用l、r记录子串范围,r-l则为两个后缀起始位置距离,若r-l>mid则符合要求(不重叠)
这里要注意的是:
1.sa对应的是差值的后缀,所以反馈到初始数组的话,ans需要+1(5个数字只有4个差值)
2.计算height的时候传入n-1,以免出现n=1的情况使得rank[0]-1=-1而出现RE
#include"cstdio" #include"queue" #include"cmath" #include"stack" #include"iostream" #include"algorithm" #include"cstring" #include"queue" #include"map" #include"set" #include"vector" #define ll long long #define mems(a,b) memset(a,b,sizeof(a)) #define ls pos<<1 #define rs pos<<1|1 using namespace std; const int MAXE = 500050; const int MAXN = 20005; const int INF = 0x3f3f3f3f; int sa[MAXN]; int X[MAXN],Y[MAXN],radix[MAXN],trank[MAXN]; int rank[MAXN]; int mus[MAXN],r[MAXN]; int n; void get_sa(int *r,int *sa,int n,int m){ int i,j,p,*Rank=X,*tsa=Y,*t; for (i=0;i<m;i++) radix[i]=0; for (i=0;i<n;i++) radix[Rank[i]=r[i]]++; for (i=1;i<m;i++) radix[i]+=radix[i-1]; for (i=n-1;i>=0;i--) sa[--radix[Rank[i]]]=i; for (j=1,p=1;p<n;j*=2,m=p) { for (p=0,i=n-j;i<n;i++) tsa[p++]=i; for (i=0;i<n;i++) if (sa[i]>=j) tsa[p++]=sa[i]-j; for (i=0;i<n;i++) trank[i]=Rank[tsa[i]]; for (i=0;i<m;i++) radix[i]=0; for (i=0;i<n;i++) radix[trank[i]]++; for (i=1;i<m;i++) radix[i]+=radix[i-1]; for (i=n-1;i>=0;i--) sa[--radix[trank[i]]]=tsa[i]; for (t=Rank,Rank=tsa,tsa=t,p=1,Rank[sa[0]]=0,i=1;i<n;i++){ if(tsa[sa[i-1]]==tsa[sa[i]]&&tsa[sa[i-1]+j]==tsa[sa[i]+j]) Rank[sa[i]]=p-1; else Rank[sa[i]]=p++; } } for(i = 0; i < n; i ++) rank[sa[i]] = i; //for(i=0;i<n;i++) cout<<height[i]<<' ';cout<<endl; } int height[MAXN]; void get_height(int *r,int *sa,int n){ int i,j=0,k=0; for(i = 0; i < n; height[rank[i ++]] = k) for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++); } bool check(int mid){ int l=sa[0],r=sa[0]; for(int i=1;i<n;i++){ if(height[i]>=mid){ l=min(l,sa[i]); r=max(r,sa[i]); if(r-l>mid) return true; } else l=r=sa[i]; } return false; } int main(){ //freopen("in.txt","r",stdin); while(scanf("%d",&n)&&n){ for(int i=0;i<n;i++) scanf("%d",&mus[i]); for(int i=0;i<n-1;i++) r[i]=mus[i+1]-mus[i]+88; r[n-1]=0; get_sa(r,sa,n,90*2); get_height(r,sa,n-1); int ans=0; int low=0,high=n,mid; while(low<=high){ mid=(low+high)>>1; if(check(mid)){ ans=mid; low=mid+1; } else high=mid-1; } if(ans<4) ans=0; else ans++; printf("%d\n",ans); } return 0; }