POJ - 1743 Musical Theme【后缀数组】【不可重叠最长重复子串】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niiick/article/details/84862315

Time limit 1000 ms
Memory limit 30000 kB

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.


题目分析

楼教主男人八题之一,也是2009年集训队论文的例题之一

题意可以化简为三点
1.在给定的字符串里找一个长度不小于5的子串
2.这个子串在字符串的另一个位置出现过,且他们不相交
3.可以"转调",即其中一个子串全部加/减某个值与另一个子串相同

先不考虑转调
可以考虑二分判断长度为mid是否可行

先求出字符串的height数组,然后在heigth上分组
每组是height上连续的一段,且其中除第一个以外所有height值都不小于mid
满足这两点的情况下使一组尽量长
比如这样(图出自2009集训队论文《后缀数组——处理字符串的有力工具》)
在这里插入图片描述

我们只需要检查是否存在一组
满足其中最大的sa-最小的sa>mid,若满足即可行
因为按这样分组,组内任意两个后缀的lcp长度都不会小于mid

现在考虑转调,其实也很简单
我们只需要在原音乐序列的差分数组上求height即可
因为若原序列有两个子段的差分序列一样,那么他们一定可以通过加/减同一个数得到


#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
typedef long long lt;

int read()
{
    int x=0,f=1;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
} 

const int maxn=40010;
int n,m;
int a[maxn];
int rak[maxn],sa[maxn],tp[maxn],tax[maxn];
int height[maxn];

void rsort()
{
    for(int i=0;i<=m;++i) tax[i]=0;
    for(int i=1;i<=n;++i) tax[rak[i]]++;
    for(int i=1;i<=m;++i) tax[i]+=tax[i-1];
    for(int i=n;i>=1;--i) sa[tax[rak[tp[i]]]--]=tp[i];
}

void ssort()
{
    m=210;
    for(int i=1;i<=n;++i)
    rak[i]=a[i],tp[i]=i;
    
    rsort();
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=n-k+1;i<=n;++i) tp[++p]=i;
        for(int i=1;i<=n;++i) if(sa[i]>k) tp[++p]=sa[i]-k;
        
        rsort();
        swap(rak,tp);
        rak[sa[1]]=p=1;
        for(int i=2;i<=n;++i)
        rak[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?p:++p;
        if(p>=n) break;
        m=p;
    }
}

void getH()
{
    int k=0;
    for(int i=1;i<=n;++i)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(a[i+k]==a[j+k]) k++;
        height[rak[i]]=k;
    }
}

int check(int x)
{
	int mx=sa[1],mi=sa[1];
    for(int i=2;i<=n;i++)
    {
        if(height[i]<x) mx=mi=sa[i];
        else
        {
            if(sa[i]<mi) mi=sa[i];
            if(sa[i]>mx) mx=sa[i];
            if(mx-mi>x) return 1;
        }
    }
    return 0;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
    	if(n==0) break;
    	for(int i=1;i<=n;++i) a[i]=read();
    	for(int i=1;i<n;++i) a[i]=a[i+1]-a[i]+90;
    	n--;//差分数组长度减1
    	
    	ssort(); getH();
    	
    	int ans=0;
		int L=0,R=n,mid;
		while(L<R)
		{
			mid=L+R>>1;
			if(check(mid)) ans=mid,L=mid+1;
			else R=mid;
		}
		if(ans<4) printf("0\n");
		else printf("%d\n",ans+1);
		//for(int i=1;i<=n;++i)
		//cout<<height[i]<<" ";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/84862315