POJ1743Musical Theme(后缀数组)

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

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

 给你一个长为n的数字序列,要你求出该序列中最长的满足下面要求的序列长度:原序列中存在不重叠的两个序列,这两个序列长度相同且将第一个序列的所有数字加上某个常数可以得到第二个序列.

所谓的音乐主题就是两端变化相同的长度大于五的不重合乐谱

比如1 2 3 4 5 6 7 8 9 10

1 2 3 4 5的变化与6 7 8 9 10就相同,且两端不重合

首先我们要求的是长度至少为5的这种不重叠序列,其实要求的这两个序列相似等价于他们两个序列中相邻元素的增幅完全一样.所以我们可以用长n的原数组的后一个数减前一个数得到长为n-1的一个新数组.

然后在这个新数组中我们只要找最长的不重叠的重复出现2次的最长连续字串即可.

上面这个问题就可以用后缀数组来解了.我们将上面的问题变成判定问题:是否存在一个长度为k的不重叠子序列?

首先对新的数组求sa和height,然后将n(新数组n-1个数,但是需要加上尾0,变成n个数)个排序后的后缀分成几组,保存每组内的height值>=k.那么如果有长为k的重复不重叠子序列,那么它一定是同一组后缀的两个前缀了.如果有一组的sa最大值与sa最小值之差>=k,那么就存在这样的序列,否则k就不合适.

AC代码:
 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdlib.h>
#include<queue>
#include<map>
#include<iomanip>
#include<math.h>
using namespace std;
typedef long long ll;
typedef double ld;
#define maxn 200080
int key[maxn],str[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int height[maxn],Rank[maxn];
void build_sa(int * s,int n,int m)
{
    int i,*x = t,*y = t2;
    for(i = 0; i < m; i++)
        c[i] = 0;
    for(i = 0; i < n; i++)
        c[ x[i] = s[i] ]++;
    for(i = 1; i < m; i++)
        c[i] += c[i-1];
    for(i = n-1; i >= 0; i--)
        sa[--c[x[i]]] = i;
    for(int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        for(i = n - k; i < n; i++)
            y[p++] = i;
        for(i = 0; i < n; i++)
            if(sa[i] >= k)
                y[p++] = sa[i] - k;
        for(i = 0; i < m; i++)
            c[i] = 0;
        for(i = 0; i < n; i++)
            c[ x[y[i]] ]++;
        for(i = 0; i < m; i++)
            c[i] += c[i-1];
        for(i = n-1; i >= 0; i--)
            sa[--c[x[y[i]]]] = y[i];
        //根据sa和y数组计算新的x数组
        swap(x,y);
        p = 1;
        x[sa[0]] = 0;
        for(i = 1; i < n; i++)
            x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1:p++;
        if(p >= n)
            break;
        m = p;
    }
}

void getHeight(int * s,int n)
{
    int i,j,k = 0;
    for(i = 0; i < n; i++)
        Rank[sa[i]] = i;
    for(i = 0; i < n; i++)
    {
        if(k)
            k--;
        int j = sa[Rank[i]-1];
        while(s[i+k] == s[j+k])
            k++;
        height[Rank[i]] = k;
    }
}

bool Judge(int n,int len)
{
    int Min = 0,Max = 0;
    for(int i = 1; i < n; i++)
    {
        if(height[i] < len)
            Min = Max = sa[i];
        else
        {
            Min = min(Min,sa[i]);
            Max = max(Max,sa[i]);
            if(Max - Min >= len)
                return 1;
        }
    }
    return 0;
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        for(int i = 0; i < n; i++)
            scanf("%d",&key[i]);
        if(n == 1)
        {
            puts("0");
            continue;
        }
        for(int i = 0; i < n-1; i++)
            str[i] = key[i+1] - key[i] + 90;
        str[n-1] = 0;
        build_sa(str,n,180);
        getHeight(str,n);
        int l = 4,r = n/2;
        int ans = 0;
        while(l <= r)
        {
            int mid = (l+r) >> 1;
            if(Judge(n,mid))
            {
                ans = mid;
                l = mid + 1;
            }
            else
                r = mid - 1;
        }
        if(ans < 4)
            printf("0\n");
        else
            printf("%d\n",ans+1);
    }
    return 0;
}

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值