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;    }}