题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:
1.长度至少为5个音符。
2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
3.重复出现的同一主题不能有公共部分。
思路:求不可重叠最长重复子串,二分法或循环遍历求长度。
二分代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAXN=20010;
int sa[MAXN];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
//的后缀的开头位置顺次放入SA中
int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
int rank[MAXN],height[MAXN];
void build_sa(int s[],int n,int m)
{
int i,j,p,*x=t1,*y=t2;
//第一轮基数排序,如果s的最大值很大,可改为快速排序
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[i]=s[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(j=1;j<=n;j<<=1)
{
p=0;
//直接利用sa数组排序第二关键字
for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=1;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
//根据sa和x数组计算新的x数组
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
if(p>=n)break;
m=p;//下次基数排序的最大值
}
}
void getHeight(int s[],int n)
{
int i,j,k=0;
for(i=0;i<=n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)k--;
j=sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
int s[MAXN];
bool check(int n,int k)//k为可能出现的子串长度
{
int Max=sa[1],Min=sa[1];
for(int i=2;i<=n;i++)
{
if(height[i]<k)Max=Min=sa[i];
else
{
if(sa[i]<Min)Min=sa[i];
if(sa[i]>Max)Max=sa[i];
if(Max-Min>k)return true;
}
}
return false;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n;
while(scanf("%d",&n)==1 && n)
{
for(int i=0;i<n;i++)scanf("%d",&s[i]);
for(int i=n-1;i>0;i--)s[i]=s[i]-s[i-1]+90;
n--;//减少一个长度
for(int i=0;i<n;i++)s[i]=s[i+1];
s[n]=0;
build_sa(s,n+1,200);
getHeight(s,n);
int ans=-1;
int l=1,r=n/2;
while(l<=r)
{
int mid=(l+r)/2;//mid为可能出现的子串长度
if(check(n,mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
if(ans<4)printf("0\n");
else printf("%d\n",ans+1);
}
return 0;
}
循环遍历代码:
#include<iostream>
#include<stdio.h>
#include<math.h>
#include <string>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std;
#define eps 1e-8
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mo(x) memset(x,0,sizeof(x))
#define ll long long int
#define ma(x,y) (x)>(y)?(x):(y)
#define mi(x,y) (x)<(y)?(x):(y)
#define mod 20071027
#define maxn 20010
#define maxm 10000001
int t1[maxn],t2[maxn],c[maxn];
bool cmp(int *r,int a,int b,int l){
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int str[],int sa[],int rankk[],int height[],int n,int m){
n++;
int i,j,p,*x=t1,*y=t2;
for(int i=0;i<m;i++) c[i]=0;
for(int i=0;i<n;i++) c[x[i]=str[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;
for(int j=1;j<=n;j<<=1){
p=0;
for(i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
if(p>=n) break;
m=p;
}
int k=0;
n--;
for(i=0;i<=n;i++) rankk[sa[i]]=i;
for(i=0;i<n;i++){
if(k) k--;
j=sa[rankk[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rankk[i]]=k;
}
}
int Rank[maxn],height[maxn];
int str[maxn];
int r[maxn],sa[maxn];
int x,n;
int main()
{
while(scanf("%d",&n)&&n){
for(int i=0;i<n;i++){
rd(str[i]);
str[i-1]=str[i]-str[i-1]+100;
//str[i]=char(x);
}
n--;
str[n]=0;
da(str,sa,Rank,height,n,200);
int res=0;
for(int i=1;i<n;i++)
{
int len=n;
for(int j=i+1;j<=n;j++)
{
len=mi(len,height[j]);
if(len<4||len<=res) break;
int kk=mi(abs(sa[i]-sa[j]),len);
res=ma(res,kk);
}
}
printf("%d\n",res==0?0:res+1);
}
return 0;
}