1. 程式人生 > >POJ1743 Musical Theme-字尾陣列

POJ1743 Musical Theme-字尾陣列

Description

題意:有 N(1 <= N <=20000) 個音符的序列來表示一首樂曲,每個音符都是 1..88 範圍內的整數,現在要找一個重複的主題。“主題” 是整個音符序列的一個子串,它需要滿足如下條件:

1. 長度至少為 5 個音符。

2. 在樂曲中重複出現。(可能經過轉調,“轉調” 的意思是主題序列中每個音符都被加上或減去了同一個整數值)

3. 重複出現的同一主題不能有公共部分。

Solution

字尾陣列經典應用, 求最長不重疊的重複子串

此題是轉調相同, 那麼拿出差分陣列來跑字尾陣列。

原陣列不重疊, 差分陣列兩個子串要隔開\(1\)個字元

然後二分長度\(x\)

, 兩個字串在一段 \(h[i] >=x\)的連續區間內, 且首字母位置相距\(>x\)

最後的原陣列重複子串的長度為 \(ans + 1\)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
using namespace std;

const int N = 2e4 + 5, inf = 1e9;

int tax[N], tmp[N], Rank[N], SA[N], rk[N], n, m, h[N], a[N]; 

int read() {
    int X = 0, p = 1; char c = getchar();
    for (; c > '9' || c < '0'; c = getchar())
        if (c == '-') p = -1;
    for (; c >= '0' && c <= '9'; c = getchar())
        X = X * 10 + c - '0';
    return X * p;
}

void radix_sort() {
    memset(tax, 0, sizeof(int) * (m + 1));
    for (int i = 1; i <= n; ++i)
        ++tax[Rank[i]];
    for (int i = 1; i <= m; ++i)
        tax[i] += tax[i - 1];
    for (int i = n; i; --i)
        SA[tax[Rank[tmp[i]]]--] = tmp[i];
}

int cmp(int x, int y, int w) {
    if (tmp[x] != tmp[y]) return 0;
    if ((x + w > n ? -1 : tmp[x + w]) == (y + w > n ? -1 : tmp[y + w])) return 1;
    return 0;
}

void get_SA() {
    for (int i = 1; i <= n; ++i)
        Rank[i] = a[i], tmp[i] = i;
    m = 233;
    radix_sort();
    for (int num = 1, w = 1; num < n; w <<= 1, m = num) {
        num = 0;
        for (int i = n - w + 1; i <= n; ++i)
            tmp[++num] = i;
        for (int i = 1; i <= n; ++i) if (SA[i] > w)
            tmp[++num] = SA[i] - w;
        radix_sort();
        for (int i = 1; i <= n; ++i)
            tmp[i] = Rank[i];
        num = Rank[SA[1]] = 1;
        for (int i = 2; i <= n; ++i)
            if (cmp(SA[i], SA[i - 1], w)) Rank[SA[i]] = num;
        else Rank[SA[i]] = ++num;
    }
}

void get_h() {
    for (int i = 1; i <= n; ++i)
        rk[SA[i]] = i;
    for (int k = 0, i = 1; i <= n; ++i) {
        if (k) k--;
        int j = SA[rk[i] - 1];
        while (a[k + i] == a[k + j]) k++;
        h[rk[i]] = k;
    }
}

void up(int &A, int B) {
    if (A < B) A = B;
}

void down(int &A, int B) {
    if (A > B) A = B;
}

int check(int x) {
    for (int i = 2, Min = inf, Max = -inf; i <= n; ++i) {
        if (h[i] >= x) {
            down(Min, SA[i]); down(Min, SA[i - 1]);
            up(Max, SA[i]); up(Max, SA[i - 1]);
            if (Max - Min > x) return 1;
        } else {
            Min = inf; Max = -inf;
        }
    }
    return 0;
}

int work() {
    n = rd; 
    if (!n) return 0;
    for (int i = 1; i <= n; ++i)
        a[i] = rd;
    for (int i = 1; i < n; ++i)
        a[i] = a[i + 1] - a[i] + 100;
    n--;
    get_SA();
    get_h();
    int l = 1, r = n, ans = 0;
    for (; l <= r;) {
        int mid = (l + r) >> 1;
        if (check(mid)) ans = mid, l = mid + 1;
        else r = mid - 1;
    }
    if (ans < 4) puts("0");
    else printf("%d\n", ans + 1); 
    return 1;
}

int main()
{
    while (work());
    return 0;
}