POJ - 1743 Musical Theme(后缀数组+二分)

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!

参考题解(syt大佬的)

  • 题意:给一段数,求最大相似子串长度,如果没有输出0。(相似子串定义:长度≥5且不重叠的每两个数值相差都相同的子串。例如:12345和45678,差值都是3.)
  • 思路:先相邻的两个数值做差,二分子串的长度,然后遍历height数组。因为排名相邻的最相似,所以相似子串长度可以尽量最长。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2e4+5, INF = 0x3f3f3f3f;
int str[maxn];
int _rank[maxn], sa[maxn], height[maxn], _temp[maxn], bucket[maxn], ary[maxn];
int n, m;   //m是自己定的,其实就是字母字符所占ascii码最大值,即桶的个数

void GetSa()
{
    for(int i = 0; i <= n; i++) _rank[i] = str[i];
    for(int i = 0; i < m; i++)  bucket[i] = 0;
    for(int i = 0; i <= n; i++) bucket[_rank[i]]++;
    for(int i = 1; i < m; i++) bucket[i] += bucket[i-1];
    for(int i = n; i >= 0; i--) sa[--bucket[_rank[i]]] = i;
    for(int _size = 1; _size <= n; _size <<= 1)
    {
        int num = 0;
        for(int i = n-_size+1; i <= n; i++) _temp[num++] = i;
        for(int i = 0; i <= n; i++) if(sa[i] >= _size)  _temp[num++] = sa[i]-_size;
        for(int i = 0; i < m; i++)  bucket[i] = 0;
        for(int i = 0; i <= n; i++) bucket[_rank[_temp[i]]]++;
        for(int i = 1; i < m; i++)  bucket[i] += bucket[i-1];
        for(int i = n; i >= 0; i--) sa[--bucket[_rank[_temp[i]]]] = _temp[i];
        swap(_rank, _temp);
        _rank[sa[0]] = 0, num = 1;
        for(int i = 1; i <= n; i++) _rank[sa[i]] = (_temp[sa[i-1]] == _temp[sa[i]] && _temp[sa[i-1]+_size] == _temp[sa[i]+_size]) ? num-1 : num++;
        m = num;
    }
}

void GetHeight()
{
    int k = 0;
    for(int i = 1; i <= n; i++) _rank[sa[i]] = i;
    for(int i = 0; i < n; i++)
    {
        if(k)   k--;
        else k = 0;
        int j = sa[_rank[i]-1];
        while(str[i+k] == str[j+k]) k++;
        height[_rank[i]] = k;
    }
}

bool judge(int k, int n)
{
    int maxx = -INF, minn = INF;
    for(int i = 2; i <= n; i++)
    {
        if(height[i] >= k)
        {
            maxx = max(maxx, max(sa[i], sa[i-1]));
            minn = min(minn, min(sa[i], sa[i-1]));
            if(maxx-minn > k)   return true;
        }
        else
        {
            maxx = -INF;    minn = INF;
        }
    }
    return false;
}

int main()
{
	while(~scanf("%d", &n), n)
    {
        for(int i = 0; i < n; i++)  scanf("%d", &str[i]);
        m = 256, n--;
        for(int i = 0; i < n; i++)  str[i] = str[i+1]-str[i]+88;
        str[n] = 0;
        GetSa();
        GetHeight();
        int l = 0, r = n>>1, ans = 0;
        while(l <= r)
        {
            int mid = (l+r)>>1;
            if(judge(mid, n))   l = mid+1, ans = mid+1;
            else r = mid-1;
        }
        if(ans >= 5)    printf("%d\n", ans);
        else printf("0\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40788897/article/details/100583136