hihoCoder1403(后缀数组 + 二分)

时间限制:5000ms
单点时限:1000ms
内存限制:256MB

描述

小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;

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章