标题效果:有一个美丽的旋律,它们是由一些不大于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; }
版权声明:本文博主原创文章,博客,未经同意不得转载。