• [POJ 1743]Musical Theme


    [POJ 1743]Musical Theme

    (Description:)

    一个长度为n的数列,问是否存在长度大于等于5的相似不重叠子列

    若存在则求最长的不重叠的相似子列,若不存在则输出0

    相似的定义为相应位置的差值为常数(如:1,2,5和3,4,6相似)

    (Solution:)

    将原数组进行差分,这样相似的子列就变成了差分数组中相同的子列

    原题目就变成了在差分数组中求最长的不重叠的相等子列

    注意,差分为相邻两个数的差值,因此差分后数组的长度为n-1,且原数组中长度为x的相似子列对应差分数组中长度为x-1的相等子列

    如果不考虑是否重叠的问题,那么这是一个经典的LCP(最长公共前缀)问题,因为每一个子串都是某个后缀的前缀

    该问题用后缀数组处理出height数组可以解决

    如果要求不重叠,我们对答案进行二分,对于每一次二分出来的答案,我们遍历每一个连续的height>=mid的区间,这段区间的字符串两两之间的最长公共前缀>=mid,在这段区间中找最靠前和最靠后的,计算两者的距离dis,如果存在某一个区间中的字符串dis>mid,则当前答案成立

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=20005;
    int s[N],ht[N],n,m,sa[N],x[N*2],y[N*2],c[N],rk[N];
    void get_sa(){
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
    	for(int i=2;i<=m;i++)c[i]+=c[i-1];
    	for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
    	for(int w=1;w<=n;w<<=1){
    		int num=0;
    		for(int i=n-w+1;i<=n;i++)y[++num]=i;
    		for(int i=1;i<=n;i++)if(sa[i]>w)y[++num]=sa[i]-w;
    		for(int i=1;i<=m;i++)c[i]=0;
    		for(int i=1;i<=n;i++)c[x[i]]++;
    		for(int i=2;i<=m;i++)c[i]+=c[i-1];
    		for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i];
    		swap(x,y);x[sa[1]]=num=1;
    		for(int i=2;i<=n;i++)
    			x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+w]==y[sa[i]+w])?num:++num;
    		if((m=num)==n)break;
    	}
    }
    bool check(int mid){
    	int mmax,mmin;
    	mmax=mmin=sa[1];
    	for(int i=2;i<=n;i++){
    		if(ht[i]>=mid){
    			mmax=max(mmax,sa[i]);
    			mmin=min(mmin,sa[i]);
    			continue;
    		}
    		if(mmax-mmin>mid)return 1;
    		else mmax=mmin=sa[i];
    	}
    	return mmax-mmin>mid;
    }
    int main(){
    	while(scanf("%d",&n)!=EOF&&n){
    		m=300;
    		for(int i=1;i<=n;i++)scanf("%d",&s[i]);n--;
    		for(int i=1;i<=n;i++)s[i]=s[i+1]-s[i]+88;
    		get_sa();
    		memset(ht,0,sizeof(ht));
    		for(int i=1;i<=n;i++)rk[sa[i]]=i;
    		for(int k=0,i=1;i<=n;i++){
    			if(k)k--;
    			while(s[i+k]==s[sa[rk[i]-1]+k])k++;
    			ht[rk[i]]=k;
    		}
    		int l=4,r=n,ans=-1;
    		while(l<=r){
    			int mid=(l+r)>>1;
    			if(check(mid)){ans=mid;l=mid+1;}
    			else r=mid-1;
    		}
    		printf("%d
    ",ans+1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    springboot启动流程(三)Environment简介
    JavaWeb【一、简介】
    JavaJDBC【七、JDBC升级版简介】
    JavaJDBC【六、连接池】
    JavaJDBC【五、事务】
    JavaJDBC【四、存储过程的使用】
    JavaJDBC【三、增删改查】
    JavaJDBC【二、Mysql包加载与使用】
    JavaJDBC【一、概述】
    Java注解【五、注解实战】
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/13490302.html
Copyright © 2020-2023  润新知