题意:给定一段由整数组成的音乐旋律的序列,每两个整数的差值就是旋律的变化,求找到原序列中最长的两个不覆盖的子串,使得两个子串的旋律变化一致(长度至少为5)
分析:把原序列相邻两两整数之间的差值构成一个新数组求出后缀数组,然后二分枚举子串的长度即可;
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 4E4+10;
int s[N],m;
int n,sa[N],rk[N],oldrk[N<<1],id[N],px[N],cnt[N],ht[N];
bool cmp(int x,int y,int w){
return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];
}
void da(int s[],int n,int m){
int i,p=0,w,k;
for(i=1;i<=n;i++) ++cnt[rk[i] = s[i]];
for(i=1;i<=m;i++) cnt[i] += cnt[i-1];
for(i=n;i>=1;i--) sa[cnt[rk[i]]--] = i;
for(w=1;w<n;w<<=1,m=p){
for(p=0,i=n;i>n-w;i--) id[++p]=i;
for(i=1;i<=n;i++)
if(sa[i]>w) id[++p]=sa[i]-w;
memset(cnt,0,sizeof(cnt));
for(i=1;i<=n;i++) ++cnt[px[i] = rk[id[i]]];
for(i=1;i<=m;i++) cnt[i] += cnt[i-1];
for(i=n;i>=1;i--) sa[cnt[px[i]]--] = id[i];
memcpy(oldrk,rk,sizeof(rk));
for(p=0,i=1;i<=n;i++)
rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
}
for(i=1,k=0;i<=n;i++){
if(k) --k;
while(s[i+k]==s[sa[rk[i]-1]+k]) ++k;
ht[rk[i]]=k;
}
}
bool check(int len){
int L,R;
L=R=-1;
for(int i=2;i<=n;i++){
if(ht[i]>=len){
if(L==-1){
L=min(sa[i-1],sa[i]);
R=max(sa[i-1],sa[i]);
}
else{
L=min(L,min(sa[i-1],sa[i]));
R=max(R,max(sa[i-1],sa[i]));
}
}
else{
if(L+len<=R) return 1;
L=R=-1;
}
}
return L+len<=R;
}
int main()
{
while(~scanf("%d",&n)&&n){
memset(cnt,0,sizeof(cnt));
int pre; scanf("%d",&pre);
for(int i=1,x;i<=n-1;i++){
scanf("%d",&x);
s[i]=x-pre+100;
pre=x;
}
n--;
da(s,n,200);
int l=4,r=n/2,ANS=-1;
while(l<=r){
int m=(l+r)>>1;
if(check(m)){
ANS=m,l=m+1;
}
else r=m-1;
}
printf("%d\n",ANS+1);
}
}