poj 1743 Musical Theme(后缀数组)

来源:互联网 发布:剑网3捏脸数据非法 编辑:程序博客网 时间:2024/03/29 08:28

Description

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

3025 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 1882 78 74 70 66 67 64 60 65 800

Sample Output

5

这就是传说中的男人八题之一,AC之后就成为了1/8个男人。

题意:求最长的一段旋律的长度。什么叫做旋律呢,就是长度至少是5,且在整个乐曲中至少出现两次,可以以不同的方式出现,什么叫以不同的方式出现呢,就是一段旋律可以通过整体加上一个正数或负数得到另一段,这两段旋律就可以认为是相同的,还有一点就是不能是有重合部分的。

转换一下,两段旋律如果是相同的,就可以通过一段加上一个数字得到另一段,也就是说两段旋律的变化规律是一样的,就是分别用后面一个音调减前面一个音调,如果两段完全相同,就认为两段旋律是同一个。这样问题就变成了求最长不可叠加前缀的问题了。答案就是转换后的数组的 最长不可叠加前缀 + 1。


这不只是要求不可重叠,还要求两个子串要隔至少一个位置,因为如果两个子串靠在一起这样反应到原串那两个子串各自的首尾是重合的。具体就是把二分里判断两个开头编号的差改为大于k,而不是大于等于k。

如果没有让两个子串是至少隔一个位置的,那么下面这组数据的答案就会出错

9

1 2 3 4 5 6 7 8 9

你就会输出5,但其实答案是0。


接下来就是二分的判断了,就是有公共前缀的后缀都是在height数组里按从前到后的顺序分成一组一组的,所以从前到后面扫height数组,并记录一下两个开头的下标就行了。


#include <cstdio>#include <cstring>#include<iostream>#include <algorithm>#define LL long longusing namespace std;const int MAXN = 50010;const int inf = 1e8;int SA[MAXN], Rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m;void RSort(){    for (int i = 0; i <= m; i ++) tax[i] = 0;    for (int i = 1; i <= n; i ++) tax[Rank[tp[i]]] ++;    for (int i = 1; i <= m; i ++) tax[i] += tax[i-1];    for (int i = n; i >= 1; i --) SA[tax[Rank[tp[i]]] --] = tp[i];}int cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w]; }void Suffix(){    for (int i = 1; i <= n; i ++) Rank[i] = a[i], tp[i] = i;    m = 256 ,RSort();    for (int w = 1, p = 1, i; p < n; w += w, m = p)    {        for (p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i;        for (i = 1; i <= n; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;        RSort(), swap(Rank, tp), Rank[SA[1]] = p = 1;        for (i = 2; i <= n; i ++) Rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;    }    int j, k = 0;    for(int i = 1; i <= n; Height[Rank[i ++]] = k)        for( k = k ? k - 1 : k, j = SA[Rank[i] - 1]; a[i + k] == a[j + k]; ++ k);}int s[MAXN],t[MAXN];void Init(){    n--;    for(int i=1;i<=n;i++)        a[i] = t[i];    Suffix();}bool judge(int k){    int mx = -inf,mi = inf;    for(int i=2;i<=n;i++)    {        if(Height[i] >= k)        {            mx = max(mx,max(SA[i],SA[i-1]));            mi = min(mi,min(SA[i],SA[i-1]));            if(mx - mi >= k)                return true;        }        else            mx = -inf,mi = inf;    }    return false;}int binarysearch(){    int l = 1,r = n;    int ans = l;    while(l <= r)    {        int mid = (l+r)/2;        if(judge(mid))        {            l = mid + 1;            ans = mid;        }        else            r = mid - 1;    }    return ans;}int main(void){    int i,j;    while(scanf("%d",&n)==1)    {        if(n == 0)            break;        for(i=1;i<=n;i++)        {            scanf("%d",&s[i]);            t[i] = s[i];        }        if(n < 10)        {            printf("0\n");            continue;        }        for(i=1;i<=n-1;i++)            t[i] = s[i+1]-s[i] + 90;        Init();        int ans = binarysearch() + 1;        printf("%d\n",ans);    }    return 0;}


原创粉丝点击