其他分享
首页 > 其他分享> > 后缀数组练习1:不可重叠最长重复子串

后缀数组练习1:不可重叠最长重复子串

作者:互联网

这道题在之前,一定要先看一下我之前在后缀数组博客里面提到的最长公共前缀

1467: 后缀数组1:不可重叠最长重复子串

poj1743

时间限制: 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

输出:

 然后如果你了解了最长公共前缀,那么我们就提一下这道题和最长公共前缀有什么大关系

求字符串中至少重复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\n");
102         else printf("%d\n",ans+1);
103         /*因为我们求的是高度差,也就是间隙,所以如果有n个点,就有n-1个间隙,所以最后结果要增加1*/
104     }
105     return 0;
106 }
Tristan Code 注释版

(非注释版,如果你完全看懂了论文,就没有必要看注释版的了)

 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\n");
92         else printf("%d\n",ans+1);
93     }
94     return 0;
95 }
Tristan Code 非注释版

 

标签:子串,cnt,后缀,20010,int,数组,sa,include,最长
来源: https://www.cnblogs.com/Tristanjiang/p/11381235.html