重复旋律2
时间限制:5000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。小Hi在练习过很多曲子以后发现很多作品自身包含一样的旋律。
旋律可以表示为一段连续的数列,相似的旋律在原数列不可重叠,比如在1 2 3 2 3 2 1 中 2 3 2 出现了一次,2 3 出现了两次,小Hi想知道一段旋律中出现次数至少为两次的旋律最长是多少?
输入
第一行一个整数 N。1≤N≤100000
接下来有 N 个整数,表示每个音的数字。1≤数字≤1000
输出
一行一个整数,表示答案。
- 样例输入
-
8 1 2 3 2 3 2 3 1
- 样例输出
-
2
此题好像不能用单调队列,所以用二分。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=400000; int Rank[maxn],cntA[maxn],cntB[maxn],A[maxn],B[maxn]; int ch[maxn],sa[maxn],tsa[maxn],ht[maxn]; int ans,N; void solve() { int i,j; for(i=1;i<=N;i++) cntA[i]=0; for(i=1;i<=N;i++) cntA[ch[i]]++; for(i=1;i<=N;i++) cntA[i]+=cntA[i-1]; for(i=N;i>=1;i--) sa[cntA[ch[i]]--]=i;//基数排序 Rank[sa[1]]=1; for(i=2;i<=N;i++) { Rank[sa[i]]=Rank[sa[i-1]]; if(ch[sa[i]]!=ch[sa[i-1]]) Rank[sa[i]]++; }//第一次排序结束。 for(int L=1;Rank[sa[N]]<N;L<<=1) { for(i=0;i<=N;i++) cntA[i]=cntB[i]=0; for(i=1;i<=N;i++) cntA[A[i]=Rank[i]]++; for(i=1;i<=N;i++) cntB[B[i]=(i+L<=N)?Rank[i+L]:0]++; for(i=1;i<=N;i++) cntA[i]+=cntA[i-1]; for(i=1;i<=N;i++) cntB[i]+=cntB[i-1]; for(i=N;i>=1;i--) tsa[cntB[B[i]]--]=i;//第二关键字排序 for(i=N;i>=1;i--) sa[cntA[A[tsa[i]]]--]=tsa[i];//按第二关键字的顺序给第一关键字排序 Rank[sa[1]]=1; for(i=2;i<=N;i++) { Rank[sa[i]]=Rank[sa[i-1]]; if(A[sa[i]]!=A[sa[i-1]]||B[sa[i]]!=B[sa[i-1]]) Rank[sa[i]]++;//扩散 } }//排序结束 for (i=1,j=0;i<=N;i++)//得到高度 { if(j) j --; while (ch[i+j]==ch[sa[Rank[i]-1]+j]) j++; ht[Rank[i]]=j; } } bool check(int x){ int Min=N,Max=0; for(int i=1;i<=N;i++){ if(ht[i]<x){ if(Max!=0&&Max-Min>=x) return true; Min=Max=sa[i]; continue; } Min=min(Min,sa[i]),Max=max(Max,sa[i]); } if(Max!=0&&Max-Min>=x) return true; return false; } void getMax() { int L=0,R=N,mid; while(L<=R)//注意这种写法是L<=R { mid=(L+R)/2; if(check(mid)) {ans=mid;L=mid+1;} else R=mid-1; } } int main() { scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&ch[i]); solve(); getMax(); printf("%d ",ans); return 0; }