A
来源:互联网 发布:李明老师的linux视频 编辑:程序博客网 时间:2024/06/06 14:23
人生第一道后缀数组题目,值得纪念下。
题目意思很简单,就是给予一个序列,求最长不相交的相同的连续子序列的长度(这里的相同,只要是两个序列的差值相等都算相同的序列)。
后缀数组模板题目:关键在于预处理上比较难想,(表示看了题解)。对于输入的序列,构造一个新的序列,该序列的每一位的值是原始序列相邻两位的差值。也就是说如果输入序列是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; }}