Musical Theme POJ
阿新 • • 發佈:2019-01-05
人生第一道字尾陣列題目,值得紀念下。
題目意思很簡單,就是給予一個序列,求最長不相交的相同的連續子序列的長度(這裡的相同,只要是兩個序列的差值相等都算相同的序列)。
字尾陣列模板題目:關鍵在於預處理上比較難想,(表示看了題解)。對於輸入的序列,構造一個新的序列,該序列的每一位的值是原始序列相鄰兩位的差值。也就是說如果輸入序列是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;
}
}