POJ 1743 Musical Theme Hash+二分法

标题效果:有一个美丽的旋律,它们是由一些不大于88音调。如果计为五个音调的量度,问:是否有相同的节奏的多个部分(相同的差,以及两者之间的相同的节奏不能重叠),并寻求最长长度。


思考:这个问题是八人中的问题,正解是自己主动机后缀,但我不会。

但是某神犇说过:“Hash大法好”。

于是这个题Hash+二分也能够解决。分析时间复杂度。2w个点,二分logn。hash挂链推断O(kn),总复杂度O(knlogn)。解决。
将原数组两两做差,然后依照这个数组hash。

二分枚举最长的同样的韵律长度。枚举每个開始的时间,然后推断两个韵律是否重叠,这个都放在hash表里即可了。


CODE:


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define BASE 177
#define MAX 20010
using namespace std;

struct HashSet{
	static const int mo = 9997;
	int head[mo + 100],total;
	int next[MAX],pos[MAX];
	unsigned long long true_hash[MAX];
	
	void Clear() {
		memset(head,0,sizeof(head));
		total = 0;
	}
	bool Insert(unsigned long long hash,int _pos,int ans) {
		int x = hash % mo;
		for(int i = head[x];i;i = next[i])
			if(true_hash[i] == hash && _pos - pos[i] > ans)
				return true;
		next[++total] = head[x];
		true_hash[total] = hash;
		pos[total] = _pos;
		head[x] = total;
		return false;
	}
}map;

unsigned long long p[MAX];
unsigned long long hash[MAX];
int cnt;
int _src[MAX],src[MAX];

void Pretreatment();
inline bool Judge(int ans);

int main()
{
	Pretreatment();
	while(scanf("%d",&cnt),cnt) {
		for(int i = 1;i <= cnt; ++i)
			scanf("%d",&_src[i]);
		for(int i = 1;i < cnt; ++i)
			src[i] = _src[i + 1] - _src[i] + 88;
		hash[0] = 0;
		for(int i = 1;i < cnt; ++i)
			hash[i] = hash[i - 1] * BASE + src[i];
		int l = 0,r = cnt,ans = 0;
		while(l <= r) {
			int mid = (l + r) >> 1;
			if(Judge(mid))
				l = mid + 1,ans = mid;
			else	r = mid - 1;
		}
		ans++;
		if(ans < 5)	ans = 0;
		printf("%d
",ans);
	}
	return 0;
}

void Pretreatment()
{
	p[0] = 1;
	for(int i = 1;i < MAX; ++i)
		p[i] = p[i - 1] * BASE;
}

inline bool Judge(int ans)
{
	map.Clear();
	for(int i = ans;i < cnt; ++i)
		if(map.Insert((unsigned long long)hash[i] - hash[i - ans] * p[ans],i,ans))
			return true;
	return false;
}


版权声明:本文博主原创文章,博客,未经同意不得转载。

原文地址:https://www.cnblogs.com/zfyouxi/p/4802529.html