后缀数组模板题
照着kuangbin大神的模板敲得~~
/*
* POJ 1743 Musical Theme
* 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。
* “主题”是整个音符序列的一个子串,它需要满足如下条件:
* 1.长度至少为5个音符
* 2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。)
* 3.重复出现的同一主题不能有公共部分。
*
* 先转化成相邻两项的差值,然后就是找不可重叠重复子串。
* 做法就是二分答案LEN
* 然后根据height值进行分组
*/
分析:
#include<cstdio>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=(s);i<(t);i++)
#define per(i,t,s) for(int i=(t);i>=(s);i--)
const int INF = 1e9 + 9;
const int N = 20000 + 9;
/********************倍增算法*后缀数组模板*******************************/
/*
*suffix array
*倍增算法 O(n*logn)
*待排序数组长度为n,放在0~n-1中,在最后面补一个0
*build_sa( ,n+1, );//注意是n+1;
*getHeight(,n);
*例如:
*n = 8;
*num[] = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
*rank[] = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
*sa[] = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
*height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
*
*/
int sa[N], t1[N], t2[N], c[N], rk[N], height[N];
void build_sa (int s[], int n, int m) {
int i, k, p, *x = t1, *y = t2;
for (i = 0; i < m; i++) c[i] = 0;
for (i = 0; i < n; i++) c[x[i] = s[i]]++;
for (i = 1; i < m; i++) c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for (k = 1; k <= n; k <<= 1) {
p = 0;
for (i = n - k; i < n; i++) y[p++] = i;
for (i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k;
for (i = 0; i < m; i++) c[i] = 0;
for (i = 0; i < n; i++) c[x[y[i]]]++;
for (i = 1; i < m; i++) c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap (x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p ++;
if (p >= n) break;
m = p;
}
}
void getHeight (int s[], int n) {
int i, j, k = 0;
for (i = 0; i <= n; i++) rk[sa[i]] = i;
for (i = 0; i < n; i++) {
if (k) k--;
j = sa[rk[i] - 1];
while (s[i + k] == s[j + k]) k++;
height[rk[i]] = k;
}
}
/********************************************************************************/
int s[N];
bool ok (int n, int k) {
int Max = sa[1], Min = sa[1];
for (int i = 2; i <= n; i++) {
if (height[i] < k) Max = Min = sa[i];
else {
if (sa[i] < Min) Min = sa[i];
if (sa[i] > Max) Max = sa[i];
if (Max - Min > k) return 1;
}
}
return 0;
}
int main() {
//freopen ("f.txt", "r", stdin);
int n;
while (~scanf ("%d", &n) && n) {
rep (i, 0, n) scanf ("%d", &s[i]);
per (i, n - 1, 1) s[i] = s[i] - s[i - 1] + 90;
// rep(i,0,n)printf("%d ",s[i]);printf("\n");
n--;
rep (i, 0, n) s[i] = s[i + 1];
s[n] = 0;
// rep(i,0,n)printf("%d ",s[i]);printf("\n");
build_sa (s, n + 1, 200);
getHeight (s, n);
int l = 1, r = n / 2;
while (l < r) {
int mid = l + (r - l + 1) / 2;
if (ok (n, mid) ) l = mid;
else r = mid - 1;
}
if (l < 4) puts ("0");
else printf ("%d\n", l + 1);
}
return 0;
}