题意:有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; }
循环遍历代码:
//#pragma comment(linker, "/STACK:10240000000,10240000000") #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; }