描述
小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;
}