POJ-1743 Musical Theme(后缀数组)

题目大意:给一个整数序列,找出最长的连续变化相同的、至少出现两次并且不相重叠一个子序列。

题目分析:二分枚举长度进行判定。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define mid (l+(r-l)/2)

const int N=20000;

int SA[N+5],height[N+5];
int *rk,*tSA;
int n,a[N+5],cnt[N+5];

bool same(int i,int j,int k)
{
    if(tSA[i]!=tSA[j]) return false;
    if(i+k>=n&&j+k>=n) return true;
    if(i+k>=n&&j+k<n) return false;
    if(i+k<n&&j+k>=n) return false;
    return tSA[i+k]==tSA[j+k];
}

void buildSA()
{
    int m=180;
    for(int i=0;i<m;++i) cnt[i]=0;
    for(int i=0;i<n;++i) ++cnt[rk[i]=a[i]];
    for(int i=1;i<m;++i) cnt[i]+=cnt[i-1];
    for(int i=n-1;i>=0;--i) SA[--cnt[rk[i]]]=i;
	
    for(int k=1;k<n;k<<=1){
        int p=0;
        for(int i=n-k;i<n;++i) tSA[p++]=i;
        for(int i=0;i<n;++i) if(SA[i]>=k) tSA[p++]=SA[i]-k;

        for(int i=0;i<m;++i) cnt[i]=0;
        for(int i=0;i<n;++i) ++cnt[rk[tSA[i]]];
        for(int i=1;i<m;++i) cnt[i]+=cnt[i-1];
        for(int i=n-1;i>=0;--i) SA[--cnt[rk[tSA[i]]]]=tSA[i];

        swap(rk,tSA);
        p=1;
        rk[SA[0]]=0;
        for(int i=1;i<n;++i){
            if(same(SA[i],SA[i-1],k)) rk[SA[i]]=p-1;
            else rk[SA[i]]=p++;
        }
        if(p>=n) break;
        m=p;
    }
}

void getHeight()
{
    for(int i=0;i<n;++i) rk[SA[i]]=i;
    int k=0;
    height[0]=0;
    for(int i=0;i<n;++i){
		if(rk[i]==0) k=0;
		else{
			if(k) --k;
			int j=SA[rk[i]-1];
			while(i+k<n&&j+k<n&&a[i+k]==a[j+k]) ++k;
			height[rk[i]]=k;
		}
    }
}

bool ok(int x)
{
	int maxn=SA[0],minn=SA[0];
	for(int i=1;i<n;++i){
		if(minn+x<maxn) return true;
		if(height[i]>=x){
			maxn=max(maxn,SA[i]);
			minn=min(minn,SA[i]);
		}else{
			maxn=minn=SA[i];
		}
	}
	return minn+x<maxn;
}

void init()
{
    rk=new int[n+5];
    tSA=new int[n+5];
}

void destroy()
{
    delete []rk;
    delete []tSA;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        init();
        for(int i=0;i<n;++i){
            scanf("%d",a+i);
            --a[i];
        }
		--n;
		int temp=a[0];
		for(int i=0;i<n;++i){
			a[i]=a[i+1]-temp+87;
			temp=a[i+1];
		}
		
        buildSA();
        getHeight();
		
		//二分枚举最短的不满足条件的
		int l=4,r=n;
		while(l<r){
			if(ok(mid)) l=mid+1;
			else r=mid;
		}
		printf("%d\n",l>=5?l:0);
        destroy();
    }
    return 0;
}

  

posted @ 2016-10-12 19:24  20143605  阅读(121)  评论(0编辑  收藏  举报