【后缀数组】[POJ 1743]Musical Theme

题目分析

后缀数组模板题目,我们可以二分一下答案然后我们在构建的每两个数的差所求得的height数组中查找当前连续大于mid的一个区间的pos最小和pos最大值得差值,然后如果这个差值大于了mid我们才认为可以使用,否则会因为有一个节点重叠(相当于每一个差值表示的是一条边,如果刚好相等,那么这两个边的一对端点是重合的)

这里有几点要注意

  1. 后缀数组时最后一个循环要反着
  2. 后缀数组在判断rank的种类是否大于等于n时要在循环底部,不应在条件中否则有可能无法进入循环
  3. 注意统计数量的数组一定要和存放n的数组大小相同

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 500000;
int sa[MAXN+10], r1[MAXN+10], r2[MAXN+10], counter[MAXN+10], height[MAXN+10];
void buildsa(int *s, int len, int kind){
    for(int i=0;i<=kind;i++) counter[i] = 0;
    for(int i=1;i<=len;i++) counter[r1[i]=s[i]]++;
    for(int i=0;i<=kind;i++) counter[i] += counter[i-1];
    for(int i=len;i>=1;i--) sa[counter[r1[i]]--] = i;
    for(int i=1;i<len;i<<=1){
        int p = 0;
        for(int j=len-i+1; j<=len; j++) r2[++p] = j;
        for(int j=1;j<=len;j++)
            if(sa[j] > i)
                r2[++p] = sa[j] - i;
        for(int j=0;j<=kind;j++) counter[j] = 0;
        for(int j=1;j<=len;j++) counter[r1[r2[j]]]++;
        for(int j=0;j<=kind;j++) counter[j] += counter[j-1];
        for(int j=len;j>=1;j--) sa[counter[r1[r2[j]]] --] = r2[j];
        swap(r1, r2);
        r1[sa[1]] = kind = 1;
        for(int j=2;j<=len;j++)
            r1[sa[j]] = (r2[sa[j]] == r2[sa[j-1]] && r2[sa[j]+i] == r2[sa[j-1]+i]) ? kind : ++kind;
        if(kind >= len) break;
    }
    int now = 0;
    for(int i=1;i<=len;i++){
        if(r1[i] == 1){
            height[r1[i]] = now = 0;
        }else{
            now = max(now-1, 0);
            int j = sa[r1[i]-1];
            while(s[i+now] == s[j+now]) now++;
            height[r1[i]] = now;
        }
    }
}
bool check(int L, int n){
    int Minpos = sa[1], Maxpos = sa[1];
    for(int i=2;i<n;i++){
        if(height[i] >= L){
            Minpos = min(Minpos, sa[i]);
            Maxpos = max(Maxpos, sa[i]);
        }else{
            if(Maxpos - Minpos > L) return true;
            Minpos = Maxpos = sa[i];
        }
    }
    return Maxpos - Minpos > L;
}
int s[MAXN+10];
int main(){
    int n;
    while(scanf("%d", &n), n){
        for(int i=1;i<=n;i++)
            scanf("%d", &s[i]);
        for(int i=2;i<=n;i++)
            s[i-1] = s[i] - s[i-1] + 100;
        s[n] = 0;
        buildsa(s, n-1, 300);
        int l = 0, r = n-1, ans=0;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(check(mid, n-1)){
                ans = mid;
                l = mid+1;
            }else r = mid-1;
        }
        if(ans < 4) printf("%d\n", 0);
        else printf("%d\n", ans+1);
    }

    return 0;
}

posted on 2016-04-07 17:53  JeremyGuo  阅读(112)  评论(0编辑  收藏  举报

导航