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;

}


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