zoukankan      html  css  js  c++  java
  • 后缀数组练习1:不可重叠最长重复子串

    这道题在之前,一定要先看一下我之前在后缀数组博客里面提到的最长公共前缀

    1467: 后缀数组1:不可重叠最长重复子串

    poj1743

    时间限制: 2 Sec  内存限制: 128 MB
    提交: 207  解决: 81
    [提交] [状态] [讨论版] [命题人:admin]

    题目描述

    题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:

        1.长度至少为5个音符。

        2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)

        3.重复出现的同一主题不能有公共部分。


    【输入格式】

    输入N(1<=N<=20000)

    输入N个正整数,有多组数据,N为0时结束。

    【输出格式】

    输出满足条件的最大长度
    【样例】
    输入:

    30
    25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
    82 78 74 70 66 67 64 60 65 80
    0

    输出:

     然后如果你了解了最长公共前缀,那么我们就提一下这道题和最长公共前缀有什么大关系

    求字符串中至少重复1次或者且不重叠的最长长度
    这里重复指的是两个字符串对应位置相减的差相等
    比如1 2 3 4 5 6 7 8 9 10,最长长度为5,因为子串1 2 3 4 5 和 6 7 8 9 10变化都一样的
    思路:既然要求变化一样,那么可以让原数组前后相减,然后利用后缀数组height的性质求子串最长公共前缀即可

    这题是要在求出相邻音高之差之后找不重叠(无公共部分)的最长的重复出现过至少两次的串,
    也就是在height数组中找到一个连续段,其各项均大于d且sa数组中的对应段中最大值最小值之差要大于d。找到最大的d即可。
    d符合一个性质,就是小的d都满足,大的d都不满足。用二分法找分界线即可。

    然后题意也非常清楚了,我们就直接讲方法:最长公共前缀+后缀数组+二分

    二分是因为我们要找最大的长度,所以要用二分

    然后还有一个要注意的就是:最长公共前缀求出来的是高度差,也就是说我们最后结果要加1,因为如果有n个点,就只有n-1个间隙,所以的话要加1才是我们需要的点数

    代码的实现

    (注释版,可能最主要的就是提及一下高度差获取的函数的使用吧,如果认真看论文应该也没有什么问题)

      1 /*求字符串中至少重复1次或者且不重叠的最长长度
      2 这里重复指的是两个字符串对应位置相减的差相等
      3 比如1 2 3 4 5 6 7 8 9 10,最长长度为5,因为子串1 2 3 4 5 和 6 7 8 9 10变化都一样的
      4 思路:既然要求变化一样,那么可以让原数组前后相减,然后利用后缀数组height的性质求子串最长公共前缀即可*/
      5  
      6 /*这题是要在求出相邻音高之差之后找不重叠(无公共部分)的最长的重复出现过至少两次的串,
      7 也就是在height数组中找到一个连续段,其各项均大于d且sa数组中的对应段中最大值最小值之差要大于d。找到最大的d即可。
      8 d符合一个性质,就是小的d都满足,大的d都不满足。用二分法找分界线即可。*/
      9 #include<cstdio>
     10 #include<cstring>
     11 #include<cstdlib>
     12 #include<algorithm>
     13 #include<cmath>
     14 #include<iostream>
     15 using namespace std;
     16 int sa[20010],Rank[20010],rsort[20010];
     17 int a[20010],cnt[20010],pos[20010],n,height[20010];/*处理最长公共前缀*/
     18 /*定义 height[i]=suffix(sa[i-1])和 suffix(sa[i])的最长公
     19 共前缀,也就是排名相邻的两个后缀的最长公共前缀*/
     20 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
     21 void get_sa(int n,int m)/*倍增模版*/
     22 {
     23     int k=1,p=0,len;
     24     for(int i=1;i<=n;i++) Rank[i]=a[i];
     25     memset(rsort,0,sizeof(rsort));
     26     for(int i=1;i<=n;i++) rsort[Rank[i]]++;
     27     for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
     28     for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i;
     29     for(int k=1;k<n;k<<=1)
     30     {
     31         len=0;
     32         for(int i=n-k+1;i<=n;i++) pos[++len]=i;
     33         for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
     34         for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]];
     35         memset(rsort,0,sizeof(rsort));
     36         for(int i=1;i<=n;i++) rsort[cnt[i]]++;
     37         for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
     38         for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i];
     39         for(int i=1;i<=n;i++) cnt[i]=Rank[i];
     40         p=1; Rank[sa[1]]=1;
     41         for(int i=2;i<=n;i++)
     42         {
     43             if(!cmp(sa[i],sa[i-1],k)) p++;
     44             Rank[sa[i]]=p;
     45         }
     46         if(p==n) break; m=p;
     47     }
     48     a[0]=0; sa[0]=0;
     49 }
     50 void get_he(int n)/*最长公共前缀*/
     51 {
     52     int j,k=0;
     53     for(int i=1;i<=n;i++)
     54     {
     55         j=sa[Rank[i]-1];/*寻找相邻的最长公共前缀*/
     56         if(k) k--;/*h[i]>=h[i-1]+1*/
     57         while(a[j+k]==a[i+k]) k++;/*寻找下一位*/
     58         height[Rank[i]]=k;/*h[i]=height[rank[i]]*/
     59     }
     60 }
     61 bool check(int k,int n)
     62 {
     63     for(int i=2;i<=n;i++)
     64     {
     65         if(height[i]<k) continue;/*如果他们的高度差小于我们设定的高度差,就找下一个*/
     66         else/*否则的话*/
     67         {
     68             for(int j=i-1;j>=1;j--)/*相邻的*/
     69             {
     70                 if(abs(sa[i]-sa[j])>k) return true;/*满足条件,返回答案,毒瘤数据考掉了重叠的,所以不能等号*/
     71                 if(height[j]<k) break;/*不满足就退出j循环*/
     72             }
     73         }
     74     }
     75     return false;
     76 }
     77 int main() 
     78 {
     79     while(scanf("%d",&n)!=EOF && n)
     80     {
     81         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
     82         int maxx=-9999999;
     83         for(int i=1;i<n;i++)
     84         { 
     85             a[i]=a[i+1]-a[i]+88;
     86             if(maxx<a[i]) maxx=a[i];/*更新最大值*/
     87         }
     88         a[n]=0; n--;
     89         get_sa(n,maxx); get_he(n);
     90         int l=1,r=n,ans=0;
     91         while(l<=r)/*二分寻找答案*/
     92         {
     93             int mid=(l+r)/2;
     94             if(check(mid,n))
     95             {
     96                 ans=mid;
     97                 l=mid+1;
     98             }
     99             else r=mid-1;
    100         }
    101         if(ans<4) printf("0
    ");
    102         else printf("%d
    ",ans+1);
    103         /*因为我们求的是高度差,也就是间隙,所以如果有n个点,就有n-1个间隙,所以最后结果要增加1*/
    104     }
    105     return 0;
    106 }
    Tristan Code 注释版

    (非注释版,如果你完全看懂了论文,就没有必要看注释版的了)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<iostream>
     7 using namespace std;
     8 int sa[20010],Rank[20010],rsort[20010];
     9 int a[20010],cnt[20010],pos[20010],n,h[20010];
    10 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
    11 void get_sa(int n,int m) 
    12 {
    13     int k=1,p=0,len;
    14     for(int i=1;i<=n;i++) Rank[i]=a[i];
    15     memset(rsort,0,sizeof(rsort));
    16     for(int i=1;i<=n;i++) rsort[Rank[i]]++;
    17     for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
    18     for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i;
    19     for(int k=1;k<n;k<<=1)
    20     {
    21         len=0;
    22         for(int i=n-k+1;i<=n;i++) pos[++len]=i;
    23         for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
    24         for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]];
    25         memset(rsort,0,sizeof(rsort));
    26         for(int i=1;i<=n;i++) rsort[cnt[i]]++;
    27         for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
    28         for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i];
    29         for(int i=1;i<=n;i++) cnt[i]=Rank[i];
    30         p=1; Rank[sa[1]]=1;
    31         for(int i=2;i<=n;i++)
    32         {
    33             if(!cmp(sa[i],sa[i-1],k)) p++;
    34             Rank[sa[i]]=p;
    35         }
    36         if(p==n) break; m=p;
    37     }
    38     a[0]=0; sa[0]=0;
    39 }
    40 void get_he(int n)
    41 {
    42     int j,k=0;
    43     for(int i=1;i<=n;i++)
    44     {
    45         j=sa[Rank[i]-1];
    46         if(k) k--;
    47         while(a[j+k]==a[i+k]) k++;
    48         h[Rank[i]]=k;
    49     }
    50 }
    51 bool check(int k,int n)
    52 {
    53     for(int i=2;i<=n;i++)
    54     {
    55         if(h[i]<k) continue;
    56         else
    57         {
    58             for(int j=i-1;j>=1;j--)
    59             {
    60                 if(abs(sa[i]-sa[j])>k) return true;
    61                 if(h[j]<k) break;
    62             }
    63         }
    64     }
    65     return false;
    66 }
    67 int main() 
    68 {
    69     while(scanf("%d",&n)!=EOF && n)
    70     {
    71         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    72         int maxx=-9999999;
    73         for(int i=1;i<n;i++)
    74         { 
    75             a[i]=a[i+1]-a[i]+88;
    76             if(maxx<a[i])maxx=a[i];
    77         }
    78         a[n]=0; n--;
    79         get_sa(n,maxx); get_he(n);
    80         int l=1,r=n,ans=1;
    81         while(l<=r)
    82         {
    83             int mid=(l+r)/2;
    84             if(check(mid,n))
    85             {
    86                 ans=mid;
    87                 l=mid+1;
    88             }
    89             else r=mid-1;
    90         }
    91         if(ans<4) printf("0
    ");
    92         else printf("%d
    ",ans+1);
    93     }
    94     return 0;
    95 }
    Tristan Code 非注释版
  • 相关阅读:
    工作感悟(一)
    laydate组件选择时间段的判断
    Win10下免安装版JDK8环境变量配置
    IDEA中lombok插件的安装
    解决加载WEB页面时,由于JS文件引用过多影响页面打开速度的问题
    Windows环境下的MYSQL5.7配置文件定位
    MYSQL使用source命令,导入SQL文件
    MYSQL5.7生成列简介及创建
    MYSQL慢查询优化方法及优化原则
    批量提取文件夹下所有目录及文件名称
  • 原文地址:https://www.cnblogs.com/Tristanjiang/p/11381235.html
Copyright © 2011-2022 走看看