Musical Theme POJ - 1743 后缀数组

A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It is unfortunate but true that this representation of melodies ignores the notion of musical timing; but, this programming task is about notes and not timings.
Many composers structure their music around a repeating &qout;theme&qout;, which, being a subsequence of an entire melody, is a sequence of integers in our representation. A subsequence of a melody is a theme if it:
  • is at least five notes long
  • appears (potentially transposed -- see below) again somewhere else in the piece of music
  • is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s)

Transposed means that a constant positive or negative value is added to every note value in the theme subsequence.
Given a melody, compute the length (number of notes) of the longest theme.
One second time limit for this problem's solutions!

Input

The input contains several test cases. The first line of each test case contains the integer N. The following n integers represent the sequence of notes.
The last test case is followed by one zero.

Output

For each test case, the output file should contain a single line with a single integer that represents the length of the longest theme. If there are no themes, output 0.

Sample Input

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

Sample Output

5

Hint

Use scanf instead of cin to reduce the read time.
 
 
题解+代码:
  1 /*
  2 对于结果我们是二分出来的
  3 首先对于原始数据我们要处理一下,因为题目上有三个要求
  4 1.长度至少为5个音符
  5 2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。)
  6 3.重复出现的同一主题不能有公共部分。
  7 
  8 我们来任意举一个例子1 2 3 2 3 4那么这个序列的输出结果应该就是3
  9 因为题目上说了可能存在转调这一种情况,那么对于音乐的主曲而言,转调之后和转调之前这个主曲相邻两个部分的差距是一样的
 10 那么我们就可以让它们减去上一个,或者减去下一个(这里以减去上一个为例)
 11 对于样例减完之后1 1 -1 1 1 然后这就解决了转调的问题,然后只需要处理1,3要求就好了
 12 对于1,3我们只需要求一下得到的这个序列中重复次数大于等于2次的主曲最长长度接可以了
 13 怎么求?
 14 可以用后缀数组中的height数组
 15 
 16 设我们二分出来的答案是x,我们只需要在确定处理后的序列中有没有长度大于等于x的两段主曲
 17 因为height[i]数组就是找后缀i和后缀i-1的最长前缀,而且最长前缀只会出现在i+1后缀和i-1后缀两者之中(至于为什么可以想想)
 18 ,所以height数组的值就表示有两个部分的长度。满足题目要求主曲部分大于等于2次出现要求
 19 
 20 但是要注意height数组中的最长前缀可能存在重叠部分,所以要判断
 21 
 22 */
 23 #include <iostream>
 24 #include <cstdio>
 25 #include <cstring>
 26 #include <algorithm>
 27 #include <queue>
 28 using namespace std;
 29 const int maxn = 20010;
 30 int sa[maxn]; //SA数组,表示将S的n个后缀从小到大排序后把排好序的
 31 //的后缀的开头位置顺次放入SA中
 32 int x[maxn], y[maxn], c[maxn];
 33 int rank[maxn], height[maxn];
 34 int s[maxn];
 35 bool pan(int *x,int i,int j,int k,int n)
 36 {
 37     int ti=i+k<n?x[i+k]:-1;
 38     int tj=j+k<n?x[j+k]:-1;
 39     return x[i]==x[j]&&ti==tj;
 40 }
 41 void build_SA(int n)
 42 {
 43     int *x=rank,*y=height,r=200;
 44     for(int i=0; i<r; i++)c[i]=0;
 45     for(int i=0; i<n; i++)c[s[i]]++;
 46     for(int i=1; i<r; i++)c[i]+=c[i-1];
 47     for(int i=n-1; i>=0; i--)sa[--c[s[i]]]=i;
 48     r=1;
 49     x[sa[0]]=0;
 50     for(int i=1; i<n; i++)
 51         x[sa[i]]=s[sa[i]]==s[sa[i-1]]?r-1:r++;
 52     for(int k=1; r<n; k<<=1)
 53     {
 54         int yn=0;
 55         for(int i=n-k; i<n; i++)y[yn++]=i;
 56         for(int i=0; i<n; i++)
 57             if(sa[i]>=k)y[yn++]=sa[i]-k;
 58         for(int i=0; i<r; i++)c[i]=0;
 59         for(int i=0; i<n; i++)++c[x[y[i]]];
 60         for(int i=1; i<r; i++)c[i]+=c[i-1];
 61         for(int i=n-1; i>=0; i--)sa[--c[x[y[i]]]]=y[i];
 62         swap(x,y);
 63         r=1;
 64         x[sa[0]]=0;
 65         for(int i=1; i<n; i++)
 66             x[sa[i]]=pan(y,sa[i],sa[i-1],k,n)?r-1:r++;
 67     }
 68     for(int i=0; i<n; i++)rank[i]=x[i];
 69 }
 70 void get_height(int n)
 71 {
 72     int i,j,k=0;
 73     for(i=1; i<=n; i++)rank[sa[i]]=i;
 74     for(i=0; i<n; i++)
 75     {
 76         if(k)k--;
 77         else k=0;
 78         j=sa[rank[i]-1];
 79         while(s[i+k]==s[j+k])k++;
 80         height[rank[i]]=k;
 81     }
 82 }
 83 int check(int n,int k)
 84 {
 85     int Max = sa[1], Min = sa[1];
 86     for (int i = 2; i <= n; i++)
 87     {
 88         if (height[i] < k)
 89             Max = Min = sa[i];
 90         else
 91         {
 92             if (sa[i] < Min) Min = sa[i];
 93             if (sa[i] > Max) Max = sa[i];
 94             if (Max - Min > k) return 1;
 95         }
 96     }
 97     return 0;
 98 }
 99 int main()
100 {
101     int n;
102     while (scanf("%d", &n) != EOF && n)
103     {
104         for (int i = 0; i < n; i++)
105             scanf("%d", &s[i]);
106         for (int i = n-1; i > 0; i--)
107             s[i] = s[i] - s[i-1] + 90;
108         n--;
109         for (int i = 0; i < n; i++)
110             s[i] = s[i+1];
111         s[n] = 0;
112         build_SA(n+1);
113         get_height(n);
114         int ans = -1;
115         int l = 1, r = n/2;
116         while (l <= r)
117         {
118             int mid = l + r >> 1;
119             if (check(n, mid))
120             {
121                 ans = mid;
122                 l = mid + 1;
123             }
124             else r = mid - 1;
125         }
126         if (ans < 4)
127             printf("0\n");
128         else printf("%d\n", ans+1);
129     }
130     return 0;
131 }
 

猜你喜欢

转载自www.cnblogs.com/kongbursi-2292702937/p/12322202.html