这道题在之前,一定要先看一下我之前在后缀数组博客里面提到的最长公共前缀
1467: 后缀数组1:不可重叠最长重复子串
时间限制: 2 Sec 内存限制: 128 MB
提交: 207 解决: 81
[提交] [状态] [讨论版] [命题人:admin]题目描述
题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:
1.长度至少为5个音符。
2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
3.重复出现的同一主题不能有公共部分。
【输入格式】输入N(1<=N<=20000)
输入N个正整数,有多组数据,N为0时结束。
【输出格式】
输出满足条件的最大长度
【样例】
输入:30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
0输出:
5
然后如果你了解了最长公共前缀,那么我们就提一下这道题和最长公共前缀有什么大关系
求字符串中至少重复1次或者且不重叠的最长长度
这里重复指的是两个字符串对应位置相减的差相等
比如1 2 3 4 5 6 7 8 9 10,最长长度为5,因为子串1 2 3 4 5 和 6 7 8 9 10变化都一样的
思路:既然要求变化一样,那么可以让原数组前后相减,然后利用后缀数组height的性质求子串最长公共前缀即可这题是要在求出相邻音高之差之后找不重叠(无公共部分)的最长的重复出现过至少两次的串,
也就是在height数组中找到一个连续段,其各项均大于d且sa数组中的对应段中最大值最小值之差要大于d。找到最大的d即可。
d符合一个性质,就是小的d都满足,大的d都不满足。用二分法找分界线即可。
然后题意也非常清楚了,我们就直接讲方法:最长公共前缀+后缀数组+二分
二分是因为我们要找最大的长度,所以要用二分
然后还有一个要注意的就是:最长公共前缀求出来的是高度差,也就是说我们最后结果要加1,因为如果有n个点,就只有n-1个间隙,所以的话要加1才是我们需要的点数
代码的实现
(注释版,可能最主要的就是提及一下高度差获取的函数的使用吧,如果认真看论文应该也没有什么问题)
1 /*求字符串中至少重复1次或者且不重叠的最长长度 2 这里重复指的是两个字符串对应位置相减的差相等 3 比如1 2 3 4 5 6 7 8 9 10,最长长度为5,因为子串1 2 3 4 5 和 6 7 8 9 10变化都一样的 4 思路:既然要求变化一样,那么可以让原数组前后相减,然后利用后缀数组height的性质求子串最长公共前缀即可*/ 5 6 /*这题是要在求出相邻音高之差之后找不重叠(无公共部分)的最长的重复出现过至少两次的串, 7 也就是在height数组中找到一个连续段,其各项均大于d且sa数组中的对应段中最大值最小值之差要大于d。找到最大的d即可。 8 d符合一个性质,就是小的d都满足,大的d都不满足。用二分法找分界线即可。*/ 9 #include<cstdio> 10 #include<cstring> 11 #include<cstdlib> 12 #include<algorithm> 13 #include<cmath> 14 #include<iostream> 15 using namespace std; 16 int sa[20010],Rank[20010],rsort[20010]; 17 int a[20010],cnt[20010],pos[20010],n,height[20010];/*处理最长公共前缀*/ 18 /*定义 height[i]=suffix(sa[i-1])和 suffix(sa[i])的最长公 19 共前缀,也就是排名相邻的两个后缀的最长公共前缀*/ 20 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];} 21 void get_sa(int n,int m)/*倍增模版*/ 22 { 23 int k=1,p=0,len; 24 for(int i=1;i<=n;i++) Rank[i]=a[i]; 25 memset(rsort,0,sizeof(rsort)); 26 for(int i=1;i<=n;i++) rsort[Rank[i]]++; 27 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 28 for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i; 29 for(int k=1;k<n;k<<=1) 30 { 31 len=0; 32 for(int i=n-k+1;i<=n;i++) pos[++len]=i; 33 for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k; 34 for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]]; 35 memset(rsort,0,sizeof(rsort)); 36 for(int i=1;i<=n;i++) rsort[cnt[i]]++; 37 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 38 for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i]; 39 for(int i=1;i<=n;i++) cnt[i]=Rank[i]; 40 p=1; Rank[sa[1]]=1; 41 for(int i=2;i<=n;i++) 42 { 43 if(!cmp(sa[i],sa[i-1],k)) p++; 44 Rank[sa[i]]=p; 45 } 46 if(p==n) break; m=p; 47 } 48 a[0]=0; sa[0]=0; 49 } 50 void get_he(int n)/*最长公共前缀*/ 51 { 52 int j,k=0; 53 for(int i=1;i<=n;i++) 54 { 55 j=sa[Rank[i]-1];/*寻找相邻的最长公共前缀*/ 56 if(k) k--;/*h[i]>=h[i-1]+1*/ 57 while(a[j+k]==a[i+k]) k++;/*寻找下一位*/ 58 height[Rank[i]]=k;/*h[i]=height[rank[i]]*/ 59 } 60 } 61 bool check(int k,int n) 62 { 63 for(int i=2;i<=n;i++) 64 { 65 if(height[i]<k) continue;/*如果他们的高度差小于我们设定的高度差,就找下一个*/ 66 else/*否则的话*/ 67 { 68 for(int j=i-1;j>=1;j--)/*相邻的*/ 69 { 70 if(abs(sa[i]-sa[j])>k) return true;/*满足条件,返回答案,毒瘤数据考掉了重叠的,所以不能等号*/ 71 if(height[j]<k) break;/*不满足就退出j循环*/ 72 } 73 } 74 } 75 return false; 76 } 77 int main() 78 { 79 while(scanf("%d",&n)!=EOF && n) 80 { 81 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 82 int maxx=-9999999; 83 for(int i=1;i<n;i++) 84 { 85 a[i]=a[i+1]-a[i]+88; 86 if(maxx<a[i]) maxx=a[i];/*更新最大值*/ 87 } 88 a[n]=0; n--; 89 get_sa(n,maxx); get_he(n); 90 int l=1,r=n,ans=0; 91 while(l<=r)/*二分寻找答案*/ 92 { 93 int mid=(l+r)/2; 94 if(check(mid,n)) 95 { 96 ans=mid; 97 l=mid+1; 98 } 99 else r=mid-1; 100 } 101 if(ans<4) printf("0 "); 102 else printf("%d ",ans+1); 103 /*因为我们求的是高度差,也就是间隙,所以如果有n个点,就有n-1个间隙,所以最后结果要增加1*/ 104 } 105 return 0; 106 }
(非注释版,如果你完全看懂了论文,就没有必要看注释版的了)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 int sa[20010],Rank[20010],rsort[20010]; 9 int a[20010],cnt[20010],pos[20010],n,h[20010]; 10 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];} 11 void get_sa(int n,int m) 12 { 13 int k=1,p=0,len; 14 for(int i=1;i<=n;i++) Rank[i]=a[i]; 15 memset(rsort,0,sizeof(rsort)); 16 for(int i=1;i<=n;i++) rsort[Rank[i]]++; 17 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 18 for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i; 19 for(int k=1;k<n;k<<=1) 20 { 21 len=0; 22 for(int i=n-k+1;i<=n;i++) pos[++len]=i; 23 for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k; 24 for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]]; 25 memset(rsort,0,sizeof(rsort)); 26 for(int i=1;i<=n;i++) rsort[cnt[i]]++; 27 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 28 for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i]; 29 for(int i=1;i<=n;i++) cnt[i]=Rank[i]; 30 p=1; Rank[sa[1]]=1; 31 for(int i=2;i<=n;i++) 32 { 33 if(!cmp(sa[i],sa[i-1],k)) p++; 34 Rank[sa[i]]=p; 35 } 36 if(p==n) break; m=p; 37 } 38 a[0]=0; sa[0]=0; 39 } 40 void get_he(int n) 41 { 42 int j,k=0; 43 for(int i=1;i<=n;i++) 44 { 45 j=sa[Rank[i]-1]; 46 if(k) k--; 47 while(a[j+k]==a[i+k]) k++; 48 h[Rank[i]]=k; 49 } 50 } 51 bool check(int k,int n) 52 { 53 for(int i=2;i<=n;i++) 54 { 55 if(h[i]<k) continue; 56 else 57 { 58 for(int j=i-1;j>=1;j--) 59 { 60 if(abs(sa[i]-sa[j])>k) return true; 61 if(h[j]<k) break; 62 } 63 } 64 } 65 return false; 66 } 67 int main() 68 { 69 while(scanf("%d",&n)!=EOF && n) 70 { 71 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 72 int maxx=-9999999; 73 for(int i=1;i<n;i++) 74 { 75 a[i]=a[i+1]-a[i]+88; 76 if(maxx<a[i])maxx=a[i]; 77 } 78 a[n]=0; n--; 79 get_sa(n,maxx); get_he(n); 80 int l=1,r=n,ans=1; 81 while(l<=r) 82 { 83 int mid=(l+r)/2; 84 if(check(mid,n)) 85 { 86 ans=mid; 87 l=mid+1; 88 } 89 else r=mid-1; 90 } 91 if(ans<4) printf("0 "); 92 else printf("%d ",ans+1); 93 } 94 return 0; 95 }