1. 程式人生 > >Musical Theme POJ

Musical Theme POJ

人生第一道字尾陣列題目,值得紀念下。
題目意思很簡單,就是給予一個序列,求最長不相交的相同的連續子序列的長度(這裡的相同,只要是兩個序列的差值相等都算相同的序列)。
字尾陣列模板題目:關鍵在於預處理上比較難想,(表示看了題解)。對於輸入的序列,構造一個新的序列,該序列的每一位的值是原始序列相鄰兩位的差值。也就是說如果輸入序列是a[i],那麼新的序列new[i] = a[i+1] - a[i];對於new序列來說如果存在長度為n的不相交的連續相同子序列的話,那麼a中一定存在n+1的滿足題意的序列。然後套字尾陣列的模板,求出height陣列,然後二分列舉答案,對於每個答案,掃一遍height陣列就可以得到答案了。
假設當前列舉的值是mid,那麼我們對於height陣列中如果存在一段連續值大於等於mid的,那麼記錄下這段連續值中sa[i]的最大值與最小值的,然後如果這兩者的差大於等於mid則成立。這是由於height記錄的相鄰陣列的最大公共字首,本來就是針對那些已經按照字典序排列好的字串,所以如果存在一段連續的height值大於mid那麼這段的字串的公共字首一定大於等於k,所以就可以用maxsa - minsa,再判斷是否相交就可以了。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define Max_N (20000 + 100)
int n;
int k;
int a[Max_N];
int rank1[Max_N];
int tmp[Max_N];
bool compare_sa(int i, int j)
{
    if(rank1[i] != rank1[j]) return rank1[i] < rank1[j];
    else
{ int ri = i + k <= n ? rank1[i + k] : -1; int rj = j + k <= n ? rank1[j + k] : -1; return ri < rj; } } void construct_sa(int buf[], int s, int sa[]) { int len = s; for (int i = 0; i <= len; i++) { sa[i] = i; rank1[i] = i < len ? buf[i] : -1
; } for ( k = 1; k <= len; k *= 2) { sort(sa, sa + len +1, compare_sa); tmp[sa[0]] = 0; for (int i = 1; i <= len; i++) { tmp[sa[i]] = tmp[sa[i-1]] + (compare_sa(sa[i-1], sa[i]) ? 1 : 0); } for (int i = 0; i <= len; i++) { rank1[i] = tmp[i]; } } } void construct_lcp(int buf[], int len, int *sa, int *lcp) { int h = 0; lcp[0] = 0; for (int i = 0; i < len; i++) { int j = sa[rank1[i] - 1]; if (h > 0) h--; for (; j + h < len && i + h < len; h++) { if (buf[j+h] != buf[i+h]) break; } lcp[rank1[i] - 1] = h; } } int sa[Max_N]; int rev[Max_N]; int a1[Max_N]; int lcp[Max_N]; int main() { while (true) { scanf("%d", &n); if (n == 0) return 0; for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n - 1; i++) a1[i] = a[i+1] - a[i] + 100; n--; construct_sa(a1, n, sa); construct_lcp(a1, n, sa, lcp); int l = 0; int r = n; int ans = 0; while (true) { int mid = l + (r - l) / 2; int flag = 0; int min1 = 20000000; int max1 = -1; for (int i = n; i >= 0; i--){ //cout << lcp[sa[i]] << endl; if (lcp[i] < mid) max1 = min1 = sa[i]; else { min1 = min(min1, sa[i]); max1 = max(max1, sa[i]); if (max1 - min1 >= mid) { flag = 1; break; } } } if (flag) { l = mid + 1; ans = mid; } else r = mid - 1; if (l > r) break; } if (ans + 1 < 5) cout << "0" << endl; else cout << ans+1 << endl; } }