题目大意:有一段优美的旋律,他们是由一些不超过88的音调组成的。若把五个音调算作一小节,问是否有超过一小节的韵律相同(差相同,且两个相同的韵律之间不能有重叠),并求这个最长的长度。
思路:这个题是男人八题之一,正解是后缀自动机,可是我不会。但是某神犇说过:“Hash大法好”。于是这个题Hash+二分也可以解决。分析时间复杂度,2w个点,二分logn,hash挂链判断O(kn),总复杂度O(knlogn),解决。
将原数组两两做差,然后按照这个数组hash。二分枚举最长的相同的韵律长度,枚举每一个开始的时间,然后判断两个韵律是否重叠,这个都放在hash表里就行了。
思路:这个题是男人八题之一,正解是后缀自动机,可是我不会。但是某神犇说过:“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\n",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;
}