zoukankan      html  css  js  c++  java
  • POJ 1743 Musical Theme(后缀数组+二分答案)

    【题目链接】 http://poj.org/problem?id=1743

     

    【题目大意】

      给出一首曲子的曲谱,上面的音符用不大于88的数字表示,
      现在请你确定它主旋律的长度,主旋律指的是出现超过一次,
      并且长度不小于5的最长的曲段,主旋律出现的时候并不是完全一样的,
      可能经过了升调或者降调,也就是说,
      是原来主旋律所包含的数字段同时加上或者减去一个数所得,
      当然,两段主旋律之间也是不能有重叠的,现在请你求出这首曲子主旋律的长度,
      如果不存在请输出0。

     

    【题解】

      首先要处理的是升调和降调的问题,由于无法确定升降的幅度,
      因此很难进行匹配,所以我们首先对输入的数组进行差值处理,
      我们发现同一个旋律的区段,它们的差值数组是相等的,
      因此,现在只要找到不重叠的长度不小于4的差值区段即可,
      由于需要求出最长的长度,考虑二分后验证可行性,二分区段的长度x,
      对差值数组求一遍后缀数组,将最长公共前缀大于等于x的划分成一组,
      如果存在一组的sa差值大于等于x,那么就表示x长度的差值数组能够被找到。
      二分结束即可得到答案。

     

    【代码】

    #include <cstdio>
    #include <cstring>
    #include <vector>
    using namespace std;
    const int N=400010;
    int n,rank[N],sa[N],h[N],tmp[N],cnt[N],ans;int s[N];
    void suffixarray(int n,int m){
        int i,j,k;n++;
        for(i=0;i<2*n+5;i++)rank[i]=sa[i]=h[i]=tmp[i]=0;
        for(i=0;i<m;i++)cnt[i]=0;
        for(i=0;i<n;i++)cnt[rank[i]=s[i]]++;
        for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
        for(i=0;i<n;i++)sa[--cnt[rank[i]]]=i;
        for(k=1;k<=n;k<<=1){
            for(i=0;i<n;i++){
                j=sa[i]-k;
                if(j<0)j+=n;
                tmp[cnt[rank[j]]++]=j;
            }sa[tmp[cnt[0]=0]]=j=0;
            for(i=1;i<n;i++){
                if(rank[tmp[i]]!=rank[tmp[i-1]]||rank[tmp[i]+k]!=rank[tmp[i-1]+k])cnt[++j]=i;
                sa[tmp[i]]=j;
            }memcpy(rank,sa,n*sizeof(int));
            memcpy(sa,tmp,n*sizeof(int));
            if(j>=n-1)break;
        }for(j=rank[h[i=k=0]=0];i<n-1;i++,k++)
        while(~k&&s[i]!=s[sa[j-1]+k])h[j]=k--,j=rank[sa[j]+1];
    }vector<int> v[N];
    bool check(int x){
        int cnt=-1;
        for(int i=1;i<=n;i++){
            if(h[i]<x)v[++cnt].clear();
            v[cnt].push_back(i);
        }for(int i=0;i<=cnt;i++){
            int L=N,R=-1;
            if(v[i].size()>1){
                for(int j=0;j<v[i].size();j++){
                    R=max(R,sa[v[i][j]]);
                    L=min(L,sa[v[i][j]]);
                }if(R-L>=x)return 1;
            }
        }return 0;
    }
    int main(){
        while(scanf("%d",&n),n){
            for(int i=0;i<n;i++)scanf("%d",&s[i]);
            for(int i=0;i<n-1;i++)s[i]=s[i+1]-s[i]+90;
            s[--n]=0; suffixarray(n,256);
            int l=0,r=n,ans=0;
            while(l<=r){
                int mid=(l+r)>>1;
                if(check(mid))ans=mid,l=mid+1;
                else r=mid-1;
            }printf("%d
    ",ans>=4?ans+1:0);
        }return 0;
    }
    

      

  • 相关阅读:
    SpringBoot实现原理
    常见Http状态码大全
    forward(转发)和redirect(重定向)有什么区别
    1094. Car Pooling (M)
    0980. Unique Paths III (H)
    1291. Sequential Digits (M)
    0121. Best Time to Buy and Sell Stock (E)
    1041. Robot Bounded In Circle (M)
    0421. Maximum XOR of Two Numbers in an Array (M)
    0216. Combination Sum III (M)
  • 原文地址:https://www.cnblogs.com/forever97/p/poj1743.html
Copyright © 2011-2022 走看看