POJ1743Musical Theme(后缀数组+二分)

题意

有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:

 1.长度至少为5个音符。

 2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)

 3.重复出现的同一主题不能有公共部分。

 

题解

因为这个问题有转调这个东西,所以当然要差分。

然后原来要求长度至少为5个字符。

因为差分变成了四个。

然后上后缀数组,二分长度设为x,根据heigth分组(当heigth<x时前面的成为一组,自己成为新的一组)。

然后在每一个有至少两个元素的组中,找到sa的最大值和最小值,如果这两个值之差大于等于x,说明至少有两个字符串不重合。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N=20100;
 8 int c[N],sa[N],s[N],x[N],y[N],n,m,height[N],rk[N],ans;
 9 void get_sa(){
10     for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
11     for(int i=1;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
13     for(int k=1;k<=n;k<<=1){
14         int num=0;
15         for(int i=n-k+1;i<=n;i++)y[++num]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[x[i]]++;
19         for(int i=1;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
21         for(int i=1;i<=n;i++){
22             swap(x[i],y[i]);
23         }
24         x[sa[1]]=1;num=1;
25         for(int i=2;i<=n;i++){
26             x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
27         }
28         if(num==n)break;
29         m=num;
30     }
31 } 
32 void get_height(){
33     int k=0;
34     for(int i=1;i<=n;i++)rk[sa[i]]=i;
35     for(int i=1;i<=n;i++){
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
40         height[rk[i]]=k;
41     }
42 }
43 bool judge(int x){
44     int mn=sa[1];int mx=sa[1];
45     for(int i=2;i<=n;i++){
46         if(height[i]<x){
47             mn=mx=sa[i];
48         }
49         else{
50             mn=min(mn,sa[i]);
51             mx=max(mx,sa[i]);
52             if(mx-mn>=x)return 1;
53         }
54     }
55     return 0;
56 }
57 int main(){
58     while(scanf("%d",&n)!=EOF){
59         memset(c,0,sizeof(c));
60         memset(sa,0,sizeof(sa));
61         memset(rk,0,sizeof(rk));
62         memset(s,0,sizeof(s));
63         memset(y,0,sizeof(y));
64         memset(height,0,sizeof(height));
65         memset(x,0,sizeof(x));
66         if(n==0)break;
67         for(int i=1;i<=n;i++){
68             scanf("%d",&s[i]);
69         }
70         n--;
71         for(int i=1;i<=n;i++){
72             s[i]=s[i+1]-s[i]+100;
73         }
74         m=200;
75         get_sa();
76         get_height();
77         int l=0;int r=n/2;
78         while(l<=r){
79             int mid=(l+r)>>1;
80             if(judge(mid)){
81                 l=mid+1;
82                 ans=mid;
83             }
84             else r=mid-1;
85         } 
86         if(ans<4)printf("0\n");
87         else printf("%d\n",ans+1);
88     }
89     return 0;
90 }

 

posted @ 2018-09-12 21:41  Xu-daxia  阅读(160)  评论(0编辑  收藏  举报