Live2d Test Env

HihoCoder1407 后缀数组二·重复旋律2

重复旋律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\n",ans);
    return 0;
}

 

posted @ 2017-11-19 10:52  nimphy  阅读(269)  评论(0)    收藏  举报