POJ1743 Musical Theme-字尾陣列
阿新 • • 發佈:2018-12-04
Description
題意:有 N(1 <= N <=20000) 個音符的序列來表示一首樂曲,每個音符都是 1..88 範圍內的整數,現在要找一個重複的主題。“主題” 是整個音符序列的一個子串,它需要滿足如下條件:
1. 長度至少為 5 個音符。
2. 在樂曲中重複出現。(可能經過轉調,“轉調” 的意思是主題序列中每個音符都被加上或減去了同一個整數值)
3. 重複出現的同一主題不能有公共部分。
Solution
字尾陣列經典應用, 求最長不重疊的重複子串
此題是轉調相同, 那麼拿出差分陣列來跑字尾陣列。
原陣列不重疊, 差分陣列兩個子串要隔開\(1\)個字元
然後二分長度\(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; }