您的位置:首页 > 理论基础 > 数据结构算法

POJ1743 Musical Theme 后缀数组

2015-12-14 21:03 483 查看

Musical Theme

典型的不可重叠最长重复子串问题。

输入处理

对输入做差分

用后一项减去前一项

1 2 4 7 -> 1 2 3

变调也认为是一样,即1 2 4 7 和 3 4 6 9是一样的

差分后长度减一

二分

二分一个k值,k表示最长公共前缀的长度

check(k)为真,则说明k可能可以更大。check(k)为假,说明k应该更小。

check验证函数

k是否让一个验证函数check为真

遍历h数组。

利用k把h数组划分为若干组,每组之间的h数组都要大于等于k

该组内任意两个前缀的最长公共前缀都大于等于k。

记录一个组内后缀下标的最大值最小值

最大值-最小值>=k 表示组内有两个串有不相交的长为k的公共前缀。

若以上条件满足,返回真。

代码

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#define N 20000
using namespace std;

int sa[N + 1], ranks[N + 1], height[N + 1];
int sz; // 后缀数组长度
int cnt[N + 1]; //基数排序的桶
int input[N + 1]; //原始数组

struct node {
int v[2]; // v[0],v[1]表示二元对。v[0]是高位,v[1]是低位。
int p; // p表示这个后缀的起始位置。
bool operator == (const node & n) const {
return v[0] == n.v[0] && v[1] == n.v[1];
}
bool operator < (const node &n) const {
if (v[0] != n.v[0]) return v[0] < n.v[0];
return v[1] < n.v[1];
}
}arr[N + 1], temp[N + 1];

void print() {
cout << "sa" << endl;
for(int i = 1; i <= sz; i++) cout << sa[i] << " ";
cout << endl << "rank" << endl;
for(int i = 1; i <= sz; i++) cout << ranks[i] << " ";
cout << endl << "height" << endl;
for(int i = 1; i <= sz; i++) cout << height[i] << " ";
cout << endl;
}

// 填入ranks数组。注意相同的后缀的排名应该相同
void ra() {
for(int i = 1, j = 1, k = 1; i <= sz; i = j, k++) {
while(j <= sz && arr[i] == arr[j]) {
ranks[arr[j++].p] = k;
}
}
}

// 基数排序 桶的范围是[0, base]
void sort(int base) {
for(int i = 1; i >= 0; i--) {
memset(cnt, 0, sizeof(int) * (base + 1));
for(int j = 1; j <= sz; j++) cnt[arr[j].v[i]]++;
for(int j = 1; j <= base; j++) cnt[j] += cnt[j - 1];
for(int j = sz; j > 0; j--) temp[cnt[arr[j].v[i]]--] = arr[j]; // 从后往前遍历,这样是稳定的
memcpy(arr, temp, sizeof(node) * (sz + 1));
}
ra();
}

void buildSA() {
for(int i = 1; i <= sz; i++) {
arr[i].p = i;
arr[i].v[0] = input[i];
arr[i].v[1] = 0;
}
sort(arr + 1, arr + sz + 1); //第一次有负数直接排序,不影响最终复杂度
ra();
for(int i = 1; i < sz; i <<= 1) {
for(int j = 1; j <= sz; j++) {
arr[j].v[0] = ranks[j];
arr[j].v[1] = j + i <= sz ? ranks[j + i] : 0;
arr[j].p = j;
}
sort(sz);
}
for(int i = 1; i <= sz; i++) {
sa[ranks[i]] = i;
}
}

void lcp() {
int len = 0;
for(int i = 1; i <= sz; i++
4000
) {
if (len > 0) len--;
if (ranks[i] > 1) {
while(input[i + len] == input[sa[ranks[i] - 1] + len]) len++;
}
height[ranks[i]] = len;
}
}

bool check(int k) {
int mx = sa[1], mn = sa[1];
for(int i = 2; i <= sz; i++) {
if(height[i] >= k) {
mx = max(mx, sa[i]);
mn = min(mn, sa[i]);
} else {
if(mx - mn >= k) return true;
mx = mn = sa[i];
}
}
return mx - mn >= k;
}

int solve() {
int lb = 0, rb = sz + 1;
while(lb + 1 < rb) {
int mid = (lb + rb) >> 1;
if (check(mid)) lb = mid;
else rb = mid;
}
return lb;
}

int main(int argc, const char * argv[]) {
while(true) {
scanf("%d", &sz);
if (sz == 0) break;
int last = 0;
for(int i = 1; i <= sz; i++) {
scanf("%d", &input[i]);
if (i > 1) input[i - 1] = input[i] - last;
last = input[i];
}
sz--;
if (sz < 5) {
cout << 0 << endl;
continue;
}
buildSA();
lcp();
//print();
int res = solve();
res = res >= 4 ? res + 1 : 0;
cout << res << endl;
}
return 0;
}


参考

http://www.cnblogs.com/zcwwzdjn/archive/2012/03/09/2388413.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息