POJ 1743:后缀数组

求满足下列要求的最长子串:

1.长度不小于5

2.出现两次(子串整体加上某个值也算出现一次

3.两次出现无重叠

 

难处理的是第二个要求:整体增加某个数也算出现一次

稍微思考一下便会得出:整体加上某个值后,相邻数的差值是不变的

所以用一个数组r[i]保存mus[i+1]与mus[i]的差值

这里要注意将差值加上88,使差值始终为正以免在计算sa、rank的时候RE,同时字符集大小也变成了88*2

 

二分答案的同时将后缀按大于mid height分组,满足要求的子串肯定是同一组内的后缀的公共前缀

用l、r记录子串范围,r-l则为两个后缀起始位置距离,若r-l>mid则符合要求(不重叠)

 

这里要注意的是:

1.sa对应的是差值的后缀,所以反馈到初始数组的话,ans需要+1(5个数字只有4个差值)

2.计算height的时候传入n-1,以免出现n=1的情况使得rank[0]-1=-1而出现RE

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"set"
#include"vector"
#define ll long long
#define mems(a,b) memset(a,b,sizeof(a))
#define ls pos<<1
#define rs pos<<1|1

using namespace std;
const int MAXE = 500050;
const int MAXN = 20005;
const int INF = 0x3f3f3f3f;

int sa[MAXN];
int X[MAXN],Y[MAXN],radix[MAXN],trank[MAXN];
int rank[MAXN];
int mus[MAXN],r[MAXN];
int n;

void get_sa(int *r,int *sa,int n,int m){
    int i,j,p,*Rank=X,*tsa=Y,*t;
    for (i=0;i<m;i++)  radix[i]=0;
    for (i=0;i<n;i++) radix[Rank[i]=r[i]]++;
    for (i=1;i<m;i++)  radix[i]+=radix[i-1];
    for (i=n-1;i>=0;i--)  sa[--radix[Rank[i]]]=i;
    for (j=1,p=1;p<n;j*=2,m=p)
    {
        for (p=0,i=n-j;i<n;i++) tsa[p++]=i;
        for (i=0;i<n;i++) if (sa[i]>=j) tsa[p++]=sa[i]-j;
        for (i=0;i<n;i++) trank[i]=Rank[tsa[i]];
        for (i=0;i<m;i++) radix[i]=0;
        for (i=0;i<n;i++) radix[trank[i]]++;
        for (i=1;i<m;i++) radix[i]+=radix[i-1];
        for (i=n-1;i>=0;i--) sa[--radix[trank[i]]]=tsa[i];
        for (t=Rank,Rank=tsa,tsa=t,p=1,Rank[sa[0]]=0,i=1;i<n;i++){
            if(tsa[sa[i-1]]==tsa[sa[i]]&&tsa[sa[i-1]+j]==tsa[sa[i]+j]) Rank[sa[i]]=p-1;
            else Rank[sa[i]]=p++;
        }
    }
    for(i = 0; i < n; i ++) rank[sa[i]] = i;
    //for(i=0;i<n;i++) cout<<height[i]<<' ';cout<<endl;
}
int height[MAXN];
void get_height(int *r,int *sa,int n){
    int i,j=0,k=0;
    for(i = 0; i < n; height[rank[i ++]] = k)
    for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
}

bool check(int mid){
    int l=sa[0],r=sa[0];
    for(int i=1;i<n;i++){
        if(height[i]>=mid){
            l=min(l,sa[i]);
            r=max(r,sa[i]);
            if(r-l>mid) return true;
        }
        else l=r=sa[i];
    }
    return false;
}

int main(){
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n)&&n){
        for(int i=0;i<n;i++) scanf("%d",&mus[i]);
        for(int i=0;i<n-1;i++) r[i]=mus[i+1]-mus[i]+88;
        r[n-1]=0;
        get_sa(r,sa,n,90*2);
        get_height(r,sa,n-1);
        int ans=0;
        int low=0,high=n,mid;
        while(low<=high){
            mid=(low+high)>>1;
            if(check(mid)){
                ans=mid;
                low=mid+1;
            }
            else high=mid-1;
        }
        if(ans<4) ans=0;
        else ans++;
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

你可能感兴趣的:(POJ 1743:后缀数组)