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相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)
- java数据结构和算法学习之汉诺塔示例