题意
有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:
1.长度至少为5个音符。
2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
3.重复出现的同一主题不能有公共部分。
题解
因为这个问题有转调这个东西,所以当然要差分。
然后原来要求长度至少为5个字符。
因为差分变成了四个。
然后上后缀数组,二分长度设为x,根据heigth分组(当heigth<x时前面的成为一组,自己成为新的一组)。
然后在每一个有至少两个元素的组中,找到sa的最大值和最小值,如果这两个值之差大于等于x,说明至少有两个字符串不重合。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N=20100; 8 int c[N],sa[N],s[N],x[N],y[N],n,m,height[N],rk[N],ans; 9 void get_sa(){ 10 for(int i=1;i<=n;i++)c[x[i]=s[i]]++; 11 for(int i=1;i<=m;i++)c[i]+=c[i-1]; 12 for(int i=n;i>=1;i--)sa[c[x[i]]--]=i; 13 for(int k=1;k<=n;k<<=1){ 14 int num=0; 15 for(int i=n-k+1;i<=n;i++)y[++num]=i; 16 for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k; 17 for(int i=1;i<=m;i++)c[i]=0; 18 for(int i=1;i<=n;i++)c[x[i]]++; 19 for(int i=1;i<=m;i++)c[i]+=c[i-1]; 20 for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0; 21 for(int i=1;i<=n;i++){ 22 swap(x[i],y[i]); 23 } 24 x[sa[1]]=1;num=1; 25 for(int i=2;i<=n;i++){ 26 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num; 27 } 28 if(num==n)break; 29 m=num; 30 } 31 } 32 void get_height(){ 33 int k=0; 34 for(int i=1;i<=n;i++)rk[sa[i]]=i; 35 for(int i=1;i<=n;i++){ 36 if(rk[i]==1)continue; 37 if(k)k--; 38 int j=sa[rk[i]-1]; 39 while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++; 40 height[rk[i]]=k; 41 } 42 } 43 bool judge(int x){ 44 int mn=sa[1];int mx=sa[1]; 45 for(int i=2;i<=n;i++){ 46 if(height[i]<x){ 47 mn=mx=sa[i]; 48 } 49 else{ 50 mn=min(mn,sa[i]); 51 mx=max(mx,sa[i]); 52 if(mx-mn>=x)return 1; 53 } 54 } 55 return 0; 56 } 57 int main(){ 58 while(scanf("%d",&n)!=EOF){ 59 memset(c,0,sizeof(c)); 60 memset(sa,0,sizeof(sa)); 61 memset(rk,0,sizeof(rk)); 62 memset(s,0,sizeof(s)); 63 memset(y,0,sizeof(y)); 64 memset(height,0,sizeof(height)); 65 memset(x,0,sizeof(x)); 66 if(n==0)break; 67 for(int i=1;i<=n;i++){ 68 scanf("%d",&s[i]); 69 } 70 n--; 71 for(int i=1;i<=n;i++){ 72 s[i]=s[i+1]-s[i]+100; 73 } 74 m=200; 75 get_sa(); 76 get_height(); 77 int l=0;int r=n/2; 78 while(l<=r){ 79 int mid=(l+r)>>1; 80 if(judge(mid)){ 81 l=mid+1; 82 ans=mid; 83 } 84 else r=mid-1; 85 } 86 if(ans<4)printf("0 "); 87 else printf("%d ",ans+1); 88 } 89 return 0; 90 }