POJ 1743 不可重叠的最长重复子串

原问题,其实是找最长的相似子串,所谓相似就是一个子串每个值加上一个偏移值可以得到另一个子串。

我们先求原数组的差值数组,对新数组求后缀数组,二分答案,判定是否有某个Height数组中的sa最小值与最大值之差大于当前枚举的子串长度。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <cassert>
#include <sstream>
using namespace std;

const int N=2e6+10010;

int sa[N];
int t1[N],t2[N],c[N];
int rk[N],height[N];

inline int cmp(int *r,int a,int b,int l){
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
int s[N];
void calcSA (int *s,int n,int m) {
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++)c[i]=0;
    for(i=0;i<n;i++)c[x[i]=s[i]]++;
    for(i=1;i<m;i++)c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1){
        p=0;
        for(i=n-j;i<n;i++)y[p++]=i;
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; // 排名从小到大,如果pos比j大,则suffix(sa[i]-j)的第二关键字为p
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[y[i]]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; // 根据第二关键字从大到小,确定新一轮sa
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}

void calcHeight(int *s,int n) {
    int i,j,k=0;
    for(i=0;i<=n;i++)rk[sa[i]]=i;
    for(i=0;i<n;i++){
        if(k)k--; // h[i]>=h[i-1]-1
        j=sa[rk[i]-1]; // suffix(j)排名在suffix(i)前一位
        while(s[i+k]==s[j+k])k++; // 暴力计算lcp
        height[rk[i]]=k;
    }
}

bool ok(int n,int m) {
    int mi=sa[1],mx=sa[1];
    for (int i=2;i<=n;i++) {
        if (height[i]<m) {
            mi=sa[i];
            mx=sa[i];
            continue;
        }
        mi=min(mi,sa[i]);
        mx=max(mx,sa[i]);
        if (mx-mi>m) return true;
    }
    return false;
}

int a[N];
int main () {
    int n;
    while (scanf("%d",&n)!=EOF,n) {
        for (int i=0;i<n;i++)
            scanf("%d",a+i);
        --n;
        for (int i=0;i<n;i++) {
            s[i]=a[i+1]-a[i]+100; // 保证正数
        }
        s[n]=0;
        calcSA(s,n+1,200);
        calcHeight(s,n);
        int l=4,r=n/2,ret=-1;
        while (l<=r) {
            int m=(l+r)>>1;
            if (ok(n,m)) {
                ret=m;
                l=m+1;
            }
            else
                r=m-1;
        }
        printf("%d\n",ret+1);
    }
    return 0;
}

 

posted @ 2015-09-18 21:52  活在夢裡  阅读(234)  评论(0编辑  收藏  举报