• POJ1743Musical Theme(后缀数组+二分)


    题意

    有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 }
  • 相关阅读:
    几种任务调度的 Java 实现方法与比较
    nginx配置
    生产消费_lock和阻塞队列
    阻塞队列
    countdownlatch+cyclicbarrier+semphore
    01背包
    skiplist
    lru
    按序打印_lock和condition
    按序打印_volatile 无法保证顺序
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/9637565.html
Copyright © 2020-2023  润新知