题目大意:给你一个序列,求其中最长的一对相似等长子串
一对合法的相似子串被定义为:
1.任意一个子串长度都大于等于5
2.不能有重叠部分
3.其中一个子串可以在全部+/-某个值后和另一个串完全相同
还是老套路了,其实只要求出每一项和前一项的差值序列,这样第三个问题可以被转化为求最长不重叠的相同子串,最后把答案+1就行了
这道题竟然还是LTC大佬的男人八题!正解是后缀数组+二分,但据说可以n^2骗分过
思路是先求出height数组,对于排序后的字符串集合有一个神奇的性质,如果有某连续的几个字符串height>=x,那么这几个字符串的公共前缀长度>=x,可以用Trie树的思想很容易地证明
那现在就要解决第二个问题,不能有重叠部分
直接求解不容易,所以我们二分答案去做,每次二分出一个答案x,然后去height数组里找,把连续的几个height>=x字符串想象成一块,那么如果这个块合法,这一块内一定存在两个字符串的sa[i]-sa[j]>=x,也就是在这一块内找出sa[i]的最大最小值就行啦
复杂度O(nlogn)
傻了吧唧的把后缀数组敲错了,调了半个小时
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N 20010 5 #define inf 0x3f3f3f3f 6 #define maxn 100 7 using namespace std; 8 9 int n,len; 10 int a[N],b[N],tr[N],rk[N],sa[N],hs[N],h[N]; 11 int gint() 12 { 13 int rett=0,fh=1;char c=getchar(); 14 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 15 while(c>='0'&&c<='9'){rett=(rett<<3)+(rett<<1)+c-'0';c=getchar();} 16 return rett*fh; 17 } 18 void clr() 19 { 20 memset(a,0,sizeof(a));memset(tr,0,sizeof(tr)); 21 memset(b,0,sizeof(b));memset(rk,0,sizeof(rk)); 22 memset(h,0,sizeof(h));memset(sa,0,sizeof(sa)); 23 memset(hs,0,sizeof(hs)); 24 } 25 bool check(int k,int x,int y){ 26 if(x+k>len||y+k>len) return 0; 27 else return (rk[x]==rk[y]&&rk[x+k]==rk[y+k])?1:0; 28 } 29 void get_sa() 30 { 31 int cnt=0,i=0; 32 for(i=1;i<=n;i++) hs[b[i]]++; 33 for(i=1;i<=2*maxn;i++) if(hs[i]) tr[i]=++cnt; 34 for(i=1;i<=2*maxn;i++) hs[i]+=hs[i-1]; 35 for(i=1;i<=n;i++) rk[i]=tr[b[i]],sa[hs[b[i]]--]=i; 36 for(int k=1;cnt<len;k<<=1) 37 { 38 for(i=1;i<=cnt;i++) hs[i]=0; 39 for(i=1;i<=len;i++) hs[rk[i]]++; 40 for(i=1;i<=cnt;i++) hs[i]+=hs[i-1]; 41 for(i=len;i>=1;i--) if(sa[i]>k) tr[sa[i]-k]=hs[rk[sa[i]-k]]--; 42 for(i=1;i<=k;i++) tr[len-i+1]=hs[rk[len-i+1]]--; 43 for(i=1;i<=len;i++) sa[tr[i]]=i; 44 for(i=1,cnt=0;i<=len;i++) tr[sa[i]]=check(k,sa[i],sa[i-1])?cnt:++cnt; 45 for(i=1;i<=len;i++) rk[i]=tr[i]; 46 } 47 } 48 void get_height() 49 { 50 for(int i=1;i<=len;i++){ 51 if(rk[i]==1) continue; 52 for(int j=max(1,h[rk[i-1]]-1);j;j++) 53 if(b[i+j-1]==b[sa[rk[i]-1]+j-1]) h[rk[i]]=j; 54 else break; 55 } 56 } 57 bool check(int ans) 58 { 59 int i=1,j=1; 60 for(i=1;i<=len;) 61 { 62 if(h[i]<ans) {i++;continue;} 63 int mi=sa[i-1],ma=sa[i-1]; 64 for(;h[i]>=ans&&i<=len;i++) 65 { 66 mi=min(mi,sa[i]); 67 ma=max(ma,sa[i]); 68 if(ma-mi>ans) return 1; 69 } 70 }return 0; 71 } 72 73 int main() 74 { 75 while(scanf("%d",&n)&&n!=0) 76 { 77 len=n; 78 clr(); 79 for(int i=1;i<=n;i++) 80 a[i]=gint(),b[i]=a[i]-a[i-1]+maxn; 81 get_sa(); 82 get_height(); 83 int l=0,r=n,ans=0; 84 while(l<=r){ 85 int mid=(l+r)>>1; 86 if(check(mid)) ans=mid,l=mid+1; 87 else r=mid-1; 88 } 89 if(ans<4) printf("0 "); 90 else printf("%d ",ans+1); 91 } 92 return 0; 93 }