描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。
小Hi在练习过很多曲子以后发现很多作品自身包含一样的旋律。旋律是一段连续的数列,相似的旋律在原数列可重叠。比如在1 2 3 2 3 2 1 中 2 3 2 出现了两次。
小Hi想知道一段旋律中出现次数至少为K次的旋律最长是多少?
输入
第一行两个整数 N和K。1≤N≤20000 1≤K≤N
接下来有 N 个整数,表示每个音的数字。1≤数字≤100
输出
一行一个整数,表示答案。
8 2 1 2 3 2 3 2 3 1样例输出
4
解题思路:首先我们二分答案,然后用后缀数组判断每个值是否合法,在height数组上从上往下扫就行。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20000 + 10;
int n, kk, m;
int a[maxn];//原数组
int rrank[maxn];//第一关键字
int Rank[maxn];//第i个前缀排第几
int tp[maxn];//第二关键字,序号为第二关键字的排名,值为对应的第一关键字的位置
int c[maxn];//用于基数排序用的数组
int Sa[maxn];//后缀数组,排名为i的后缀是哪一个后缀
int Height[maxn];//排名为i的后缀与排名为i - 1的后缀的lcp
bool judge(int *x, int loc, int ww)
{
if(x[Sa[loc]] == x[Sa[loc - 1]] && x[Sa[loc] + ww] == x[Sa[loc - 1] + ww]) return true;
else return false;
}
void suffix()
{
for(int i = 1; i <= n; i++) rrank[i] = a[i];
for(int i = 1; i <= n; i++) tp[i] = i;
for(int i = 0; i <= 100; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rrank[tp[i]]]++;
for(int i = 1; i <= 100; i++) c[i] += c[i - 1];
for(int i = n; i >= 1; i--) Sa[c[rrank[tp[i]]]--] = tp[i];
m = 100;
for(int w = 1, p = 0; w <= n; w += w, m = p)
{
p = 0;
for(int i = n - w + 1; i <= n; i++) tp[++p] = i;
for(int i = 1; i <= n; i++)
{
if(Sa[i] > w) tp[++p] = Sa[i] - w;
}
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rrank[tp[i]]]++;
for(int i = 1; i <= m; i++) c[i] += c[i - 1];
for(int i = n; i >= 1; i--) Sa[c[rrank[tp[i]]]--] = tp[i];
for(int i = 1; i <= n; i++) tp[i] = rrank[i];//重新计算rank的值
rrank[Sa[1]] = 1;
p = 1;
for(int i = 2; i <= n; i++)
{
if(judge(tp, i, w)) rrank[Sa[i]] = p;
else rrank[Sa[i]] = ++p;
}
}
for(int i = 1; i <= n; i++)
{
Rank[Sa[i]] = i;
}
int k = 0;
Height[1] = 0;
for(int i = 1; i <= n; i++)
{
if(k) k--;
while(a[i + k] == a[Sa[Rank[i] - 1] + k])
{
k++;
}
Height[Rank[i]] = k;
}
}
bool ok(int mid)
{
int sum = 1;
for(int i = 2; i <= n; i++)
{
if(Height[i] >= mid)
{
sum++;
}
else
{
if(sum >= kk) return true;
sum = 1;
}
}
return false;
}
int main()
{
scanf("%d%d", &n, &kk);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
suffix();
int l = 0;
int r = n;
int re = l;
while(l <= r)
{
int mid = (l + r)>>1;
if(ok(mid))
{
l = mid + 1;
re = mid;
}
else r = mid - 1;
}
printf("%d\n", re);
return 0;
}