【POJ No. 1743】音乐主题 Musical Theme
北大OJ 题目地址

【题意】音乐旋律被表示为N (1≤N ≤20000)个音符的序列,它们是[1, 88]内的整数,每个音符都代表钢琴上的一个键。许多作曲家都围绕一个重复的主题谱写音乐,该主题属于整个旋律的子序列。旋律的子序列是一个主题,若满足至少5个音符而且在音乐片段的其他地方再次出现(不重叠,但可能存在转换,转换是指该子序列中的每个音符都同时加上或减去一个值),则给定一个旋律,计算最长主题的长度(音符数)。
【输入输出】
输入:
输入包含多个测试用例,每个测试用例的第1行都包含整数N 。以下N 个整数表示音符序列。最后一个测试用例后跟一个0。
输出:
对每个测试用例,都单行输出最长主题的长度。若没有主题,则输出0。
【样例】

【思路分析】
这道题求解的是不重叠、长度大于或等于5的最长重复子串的长度,可以先转变为子串问题,再采用后缀数组及二分法求解。
因为主题子序列可能同时加上或减去一个数,如34 30 26 22 18,若同时加上48,则转换为82 78 74 70 66,因此可以将数字序列逐项求差,转变为普通的子串问题。在差值序列上求解不重叠、长度大于或等于4的最长重复子串的长度ans,因为求差序列比原序列长度少1,所以需要输出ans+1。
例如,对输入样例数据逐项求差后(从第2个开始,每个数都减去前一个数),序列如下:

不重叠长度大于或等于4的最长重复子串为-4-4-4-4,其长度为4,原序列是34 30 26 22 18,长度为5。
【算法设计】
① 逐项求差,将问题转变为普通的求子串问题。
② 求解sa数组。
③ 求解rank数组和height数组。
④ 使用二分法求解,对特定的长度mid,判断是否满足height[i ]≥mid,且sa的最大、最小差值也大于或等于mid(保证不重叠)。
【算法实现】
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=20010;
const int maxm=200;
int n,k; 
int s[maxn],ss[maxn],sa[maxn],rank[maxn],height[maxn];;
int wa[maxn],wb[maxn],wv[maxn],c[maxm];
int cmp(int *r,int a,int b,int l){
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb;
    for(i=0;i<m;i++) 
        c[i]=0;
    for(i=0;i<n;i++) 
        c[x[i]=r[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(k=1;k<=n;k<<=1){
        //直接利用sa排序第二关键字 
		p=0;
		for(i=n-k;i<n;i++) 
            y[p++]=i;//补零的位置下标排在最前面 
        for(i=0;i<n;i++)
            if(sa[i]>=k)
                y[p++]=sa[i]-k;
        //基数排序第一关键字
        for(i=0;i<n;i++)
            wv[i]=x[y[i]];//将第二关键字排序结果转换为名次,进行排序 
        for(i=0;i<m;i++)
            c[i]=0;
        for(i=0;i<n;i++)
            c[wv[i]]++;
        for(i=1;i<m;i++)
            c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)
            sa[--c[wv[i]]]=y[i];
		//根据sa和x数组,重新计算新的x数组
        swap(x,y);//y数组已经没有用,更新x需要使用x本身数据,因此放入y使用 
		p=1,x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
    	if(p>=n)//排序结束 
    		break;
    	m=p;
    }
}
void calheight(int *r,int *sa,int n){
    int i,j,k=0;
    for(i=1;i<=n;i++)
		rank[sa[i]]=i;
    for(i=0;i<n;i++){
    	if(k)
    		k--;
    	j=sa[rank[i]-1];
    	while(r[i+k]==r[j+k])
    		k++;
    	height[rank[i]]=k;
	}
}
bool check(int mid){
	int mx=sa[1],mn=sa[1];
	for(int i=2;i<=n;i++){
		if(height[i]>=mid){
			mx=max(mx,sa[i]);
			mn=min(mn,sa[i]);
			if(mx-mn>=mid)
				return 1;
		}	
		else{
			mx=sa[i];
			mn=sa[i];
		}
	}
	return 0;
}
void solve(){
	int L=4,R=n,res=-1;
	while(L<=R){
		int mid=(L+R)>>1;
		if(check(mid)){
			res=mid;
			L=mid+1;
		}
		else
			R=mid-1;
	}
	if(res<4)
		printf("0\n");
	else
		printf("%d\n",res+1);
}
int main(){
	
	while(~scanf("%d",&n),n){
		for(int i=0;i<n;i++)
			scanf("%d",&s[i]);
		if(n<9){
			printf("0\n");
			continue;
		}
		n--;
		for(int i=0;i<n;i++)
			ss[i]=s[i+1]-s[i]+100;
		ss[n]=0;
		da(ss,sa,n+1,200);
		calheight(ss,sa,n);
		solve();
	}
	
	return 0;
}












![[附源码]Node.js计算机毕业设计电影售票管理系统Express](https://img-blog.csdnimg.cn/cebd50264dde49abb772efd0d12bfdb1.png)







