【POJ1743】Musical Themes 乐曲主题 后缀数组、 (也可以用hash+二分做的~)

题意:

1829: Musical Themes 乐曲主题

Time Limit: 1 Sec  Memory Limit: 128 MB

Submit: 42  Solved: 15

[Submit][Status][Web
Board
]

Description

我们用N(1 <= N <=5000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,每个数表示钢琴上的一个键。很不幸这种表示旋律的方法忽略了音符的时值,但这项编程任务是关于音高的,与时值无关。

许多作曲家围绕一个重复出现的“主题”来构建乐曲。在我们的乐曲表示法中,“主题”是整个音符序列的一个子序列,它需要满足如下条件:

  • 长度至少为5个音符
  • 在乐曲中重复出现(可能经过转调,见下)
  • 重复出现的同一主题不能重叠

“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。

给定一段乐曲,计算其中最长主题的长度(即音符数)。

Input

输出文件的第一行包含整数N。下面的每一行(最后一行可能除外)包含20个整数,表示音符序列。最后一行可能少于20个音符。

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 80

Sample Output

5

HINT

样例中这个长度为5的主题是输入文件中第一行的最后5个音符和第二行开头5个音符

Source

USACO

Best Solution

C C++ Pascal
NULL 80643.wyfcyx

(0ms,1100KB,2168B)

21804.Ares

(72ms,292KB,561B)

[Submit][Status][Web
Board
]




Powered by LLQ.   ?2012-2014 长春吉大附中实验学校.

题解:

啊,写个后缀数组,然后二分check时根据height分组,使每一段都满足height足够长,扫一遍,如果里面离的最远两个没重合(sa的差足够大),就return 1。。。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 21000
using namespace std;
int s[N];
int sa[N],rank[N],h[N],n,m,len;
int cnt[N],val[N],stk[N],_val[N],top;
bool issame(int a,int b,int hl)
{
	return val[a]==val[b]&&
	((a+hl>=len&&b+hl>=len)||(a+hl<len&&b+hl<len&&val[a+hl]==val[b+hl]));
}
void SA(int lim)
{
	int i,j,k,hl;
	for(i=0;i<lim;i++)cnt[i]=0;
	for(i=0;i<len;i++)cnt[val[i]=s[i]]++;
	for(i=1;i<lim;i++)cnt[i]+=cnt[i-1];
	for(i=len-1;i>=0;i--)sa[--cnt[val[i]]]=i;

	for(k=1;;k++)
	{
		top=0,hl=1<<(k-1);
		for(i=0;i<len;i++)if(sa[i]+hl>=len)stk[top++]=sa[i];
		for(i=0;i<len;i++)if(sa[i]>=hl)stk[top++]=sa[i]-hl;

		for(i=0;i<lim;i++)cnt[i]=0;
		for(i=0;i<len;i++)cnt[val[i]]++;
		for(i=1;i<lim;i++)cnt[i]+=cnt[i-1];
		for(i=len-1;i>=0;i--)sa[--cnt[val[stk[i]]]]=stk[i];

		for(lim=i=0;i<len;lim++)
		{
			for(j=i;j<len-1&&issame(sa[j],sa[j+1],hl);j++);
			for(;i<=j;i++)_val[sa[i]]=lim;
		}
		for(i=0;i<len;i++)val[i]=_val[i];
		if(lim==len)break;
	}

	for(i=0;i<len;i++)rank[sa[i]]=i;
	for(k=i=0;i<len;i++)
	{
		if(k)k--;
		if(!rank[i])continue;
		while(s[i+k]==s[sa[rank[i]-1]+k])k++;
		h[rank[i]]=k;
	}
}
bool check(int mid)
{
	int l=sa[0],r=sa[0],i;
	for(i=0;i<len;i++)
	{
		if(h[i]<mid)
		{
			l=r=sa[i];
			continue;
		}
		l=min(l,sa[i]);
		r=max(r,sa[i]);
		if(r-l>mid)return 1;
	}
	return 0;
}
int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k;
	scanf("%d",&len);
	scanf("%d",&s[0]);
	for(i=1;i<len;i++)scanf("%d",&s[i]),s[i-1]=s[i]-s[i-1]+100;
	SA(256);
	int l=0,r=len,mid,ans=0;
	while(l<=r)
	{
		if(r-l<=3)
		{
			for(i=l;i<=r;i++)if(check(i))ans=i;
				break;
		}
		mid=l+r>>1;
		if(check(mid))l=mid;
		else r=mid-1;
	}
	if(ans>=4)printf("%d\n",ans+1);else puts("0");
	return 0;
}
时间: 2024-08-01 16:08:14

【POJ1743】Musical Themes 乐曲主题 后缀数组、 (也可以用hash+二分做的~)的相关文章

2019 CCPC 网络赛第三题 K-th occurrence 后缀数组+划分树+ST表+二分

题意:给你一个长度为n的字符串,每次询问给出三个数:L , R , K,表示原串 L 到 R 的子串在原串第K次出现的首字母的位置 解题思路:对子串的大量操作,不难想到后缀数组(后缀树/后缀自动机不会,所以没想到),注意到子串s[L.....R]必然是某一个后缀的前缀,所以所有前缀是该子串的后缀的排名(即rank数组的值)必定连续,也就是说在后缀数组(sa数组)中,下标是连续的,那么就是求区间第K大了(因为sa数组的值代表的是在字符串中的位置)(这里区间第K大我用划分树求),至于这一段区间的起点

[POJ1743] 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 tim

[USACO5.1]乐曲主题Musical Themes

题目链接:戳我 Emmm......hash怎么做啊不会啊 这里是SA后缀数组版本的 就是先两两做差分,作为要处理后缀的数组.普通地求出来h数组之后,我们二分这个答案,然后判定是否合法就行了.是否合法即\(min(sa[j])+h[i]<max(sa[j]),j<=i\) 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include

【后缀数组】总结

  总结了几天的后缀数组,终于完成了. 不可重叠重复串; 可重叠k次重复串; 不相同子串个数 最长回文子串 连续重复子串 重复次数最多的连续重复子串 最长公共子串 长度不小于k 的公共子串的个数 不小于k 个字符串中的最长子串 出现或反转后出现在每个字符串中的最长子串 3个字符串,问其中公共子串分别为1,2,3...l的有多少个 快速通道:http://poj.org/problem?id=1743 题意:有N(1<= N<=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内

【经典数据结构】后缀数组

转自:http://www.acmerblog.com/suffix-array-6150.html 在字符串处理当中,后缀树和后缀数组都是非常有力的工具,其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料.其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多. 后缀树组是一个字符串的所有后缀的排序数组.后缀是指从某个位置 i 开始到整个串末尾结束的一个子串.字符串 r 的从 第 i 个字

poj 3518 Corporate Identity 后缀数组-&gt;多字符串最长相同连续子串

题目链接 题意:输入N(2 <= N <= 4000)个长度不超过200的字符串,输出字典序最小的最长公共连续子串; 思路:将所有的字符串中间加上分隔符,注:分隔符只需要和输入的字符不同,且各自不同即可,没有必要是最小的字符; 连接后缀数组求解出height之后二分长度,由于height是根据sa数组建立的,所以前面符合的就是字典序最小的,直接找到就停止即可; ps: 把之前的模板简化了下,A题才是关键; #include<iostream> #include<cstdio&

POJ3294 Life Forms(二分+后缀数组)

给n个字符串,求最长的多于n/2个字符串的公共子串. 依然是二分判定+height分组. 把这n个字符串连接,中间用不同字符隔开,跑后缀数组计算出height: 二分要求的子串长度,判断是否满足:height分组,统计一个组不同的字符串个数是否大于n/2: 最后输出方案,根据二分得出的子串长度的结果,直接再遍历一遍height,因为这儿是有序的后缀所以找到一个就直接输出. 1 #include<cstdio> 2 #include<cstring> 3 #include<cm

UVA 11475 后缀数组/KMP

题目链接: 题意:给定一个只含字母的字符串,求在字符串末尾添加尽量少的字符使得字符串为回文串. 思路:因为只能从末尾添加字符,所以其实求的是最长的后缀回文串.那么添加的字符为除了这个原串的最长后缀回文串之外的其他字符.于是问题就转变成了求字符串的最长后缀回文串,对于后缀数组求回文串子串的做法,将整个字符串反过来写在原字符串后面,中间用一个特殊的字符隔开.这样就把问题变为了求这个新的字符串的某两个后缀的最长公共前缀.奇数长度和偶数长度的分开做.对于求奇数长度,假设现在枚举的位置为i,那么对应在反过

后缀数组(至少重复k次的可重叠的最长重复子串)—— POJ 3882

对应POJ 题目:点击打开链接 Stammering Aliens Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Dr. Ellie Arroway has established contact with an extraterrestrial civilization. However, all efforts to decode their m