poj 1743 Musical Theme

Musical Theme

 POJ - 1743 

题目大意:

给定一个数组,求最长重复子串,这两个子串不能重叠。

/*
    后缀数组不用说了
    首先最长允许重复的子串就是max{height}
    不允许重复也就是限制了两字符串之间的距离,可以二分答案来做
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 22222
#define INF (1<<30)
using namespace std;
int n,wa[maxn],wb[maxn],c[maxn],sa[maxn],rk[maxn],h[maxn],a[maxn],s[maxn];
void SA(int *s,int n,int m){
    int *x=wa,*y=wb;
    for(int i=0;i<m;i++)c[i]=0;
    for(int i=0;i<n;i++)c[x[i]=s[i]]++;
    for(int i=1;i<m;i++)c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    int k=1,p=0;
    while(k<=n){
        for(int i=n-k;i<n;i++)y[p++]=i;
        for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        for(int i=0;i<m;i++)c[i]=0;
        for(int i=0;i<n;i++)c[x[y[i]]]++;
        for(int i=1;i<m;i++)c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);x[sa[0]]=0;p=1;
        for(int i=1;i<n;i++){
            if((y[sa[i-1]]==y[sa[i]])&&((y[sa[i-1]+k]==y[sa[i]+k]&&sa[i]+k<n&&sa[i-1]+k<n)||(sa[i]+k>=n&&sa[i-1]+k>=n)))x[sa[i]]=p-1;
            else x[sa[i]]=p++;
        }
        if(p>=n)break;
        m=p;p=0;k<<=1;
    }
    for(int i=1;i<n;i++)rk[sa[i]]=i;
    k=0;
    for(int i=0;i<n-1;h[rk[i++]]=k){
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
    }
}
bool check(int mid){
    bool flag=0;
    int mn=INF,mx=-INF;
    for(int i=2;i<=n;i++){
        if(h[i]>=mid){
            mn=min(mn,min(sa[i],sa[i-1]));
            mx=max(mx,max(sa[i],sa[i-1]));
            if(mx-mn>mid)return 1;
        }
        else{mx=-INF;mn=INF;}
    }
    return 0;
}
int main(){
    while(1){
        scanf("%d",&n);
        if(n==0)return 0;
        memset(s,0,sizeof(s));
        memset(sa,0,sizeof(sa));
        memset(rk,0,sizeof(rk));
        memset(h,0,sizeof(h));
        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]+88;
        s[n]=0;
        SA(s,n+1,176);
        int l=0,r=n>>1;
        while(l<r){
            int mid=(l+r+1)>>1;
            if(check(mid))l=mid;
            else r=mid-1;
        }
        if(l>=4)printf("%d\n",l+1);
        else puts("0");
    }
    return 0;
}

 

posted @ 2018-01-21 16:56  Echo宝贝儿  阅读(170)  评论(0编辑  收藏  举报