-
题目概述:
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.
给你一段乐章,乐章由n个音符组成,这些音符由1..88的整数表示。现在你需要找出最长的一段旋律,使得这一段旋律至少有5个音符,至少出现过两次,且这两次的位置不相交(如果两段旋律经过变调后是一样的,那么这两段旋律可以看成出现过两次)。
-
输入格式:
输入包含多组数据,每组数据的第一行包括一个整数n,表示乐章的长度。接下来一行包括n个整数,表示每个音符。
-
输出格式:
输出一个整数,表示满足题意的最长的旋律的长度。
-
样例输入:
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
-
思路:
表示...字符串的题真心鬼畜。半天%完罗穗骞的《后缀数组——处理字符串的有力工具》后,彻底滚粗(kmp,心中永远的痛)。
先做完了UOJ#35的模板题,又调了半天,内心是崩溃的。
于是开始lg这道题。
一道后缀自动机的模板题。用相邻两个数的差构造新的串,对新串用后缀数组lg一下,然后二分一下求不相交子串的最大长度。
注意n=1的情况(可以和n<10的情况一起判了)。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int MAXN = 20005; 6 int s[MAXN], sx[MAXN], sy[MAXN], ssum[MAXN], sa[MAXN], rk[MAXN], h[MAXN]; 7 inline bool cmp(int* y, int i, int j, int k) { 8 return y[i] == y[j] && y[i + k] == y[j + k]; 9 } 10 11 void getsa(int n, int m) { 12 int i, j, p, *x = sx, *y = sy; 13 for(i = 0; i < m; ++i) ssum[i] = 0; 14 for(i = 0; i < n; ++i) ++ssum[x[i] = s[i]]; 15 for(i = 1; i < m; ++i) ssum[i] += ssum[i - 1]; 16 for(i = n - 1; ~i; --i) sa[--ssum[x[i]]] = i; 17 for(p = j = 1; p < n; j <<= 1, m = p) { 18 for(p = 0, i = n - j; i < n; ++i) y[p++] = i; 19 for(i = 0; i < n; ++i) if(sa[i] >= j) y[p++] = sa[i] - j; 20 for(i = 0; i < m; ++i) ssum[i] = 0; 21 for(i = 0; i < n; ++i) ++ssum[x[y[i]]]; 22 for(i = 1; i < m; ++i) ssum[i] += ssum[i - 1]; 23 for(i = n - 1; ~i; --i) sa[--ssum[x[y[i]]]] = y[i]; 24 swap(x, y); x[sa[0]] = 0; 25 for(p = i = 1; i < n; ++i) x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; 26 } 27 } 28 29 void geth(int n) { 30 int j, k = 0; 31 for(int i = 1; i <= n; ++i) rk[sa[i]] = i; 32 for(int i = 0; i < n; h[rk[i++]] = k) 33 for(k ? k-- : 0, j = sa[rk[i] - 1]; s[i + k] == s[j + k]; ++k); 34 } 35 36 int solve(int x, int n) { 37 int i = 2, minh, maxh; 38 while(1) { 39 while(i <= n && h[i] < x) ++i; 40 if(i > n) break; 41 maxh = minh = sa[i - 1]; 42 while(i <= n && h[i] >= x) { 43 minh = min(minh, sa[i]); maxh = max(maxh, sa[i++]); 44 } 45 if(maxh - minh > x) return true; 46 } 47 return false; 48 } 49 50 int main() { 51 int n; 52 while(~scanf("%d", &n) && n) { 53 memset(sx, 0, sizeof(sx)); 54 memset(sy, 0, sizeof(sy)); 55 memset(sx, 0, sizeof(sa)); 56 memset(ssum, 0, sizeof(ssum)); 57 for(int i = 0; i < n; ++i) scanf("%d", s + i); 58 if(--n < 9) { puts("0"); continue; } 59 for(int i = 0; i < n; ++i) s[i] = s[i + 1] - s[i] + 90; 60 s[n] = 0; 61 getsa(n + 1, 200); geth(n); 62 int l = 1, r = (n >> 1) + 1; 63 while(l < r - 1) { 64 int mid = l + r >> 1; 65 if(solve(mid, n)) l = mid; 66 else r = mid; 67 } 68 printf("%d\n", l < 4 ? 0 : r); 69 } 70 return 0; 71 }
(话说论文里的变量名好奇怪)
抱歉博客更晚了,以后应该可以按时更新。