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

猜你喜欢

转载自www.cnblogs.com/cychester/p/10064162.html