• poj 3261 Milk Patterns(后缀数组)(k次的最长重复子串)


    Milk Patterns
    Time Limit: 5000MS   Memory Limit: 65536K
    Total Submissions: 7938   Accepted: 3598
    Case Time Limit: 2000MS

    Description

    Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some regular patterns in the daily milk quality.

    To perform a rigorous study, he has invented a complex classification scheme by which each milk sample is recorded as an integer between 0 and 1,000,000 inclusive, and has recorded data from a single cow overN (1 ≤ N ≤ 20,000) days. He wishes to find the longest pattern of samples which repeats identically at least K (2 ≤ K  N) times. This may include overlapping patterns -- 1 2 3 2 3 2 3 1 repeats 2 3 2 3 twice, for example.

    Help Farmer John by finding the longest repeating subsequence in the sequence of samples. It is guaranteed that at least one subsequence is repeated at least K times.

    Input

    Line 1: Two space-separated integers: N and K  Lines 2..N+1: N integers, one per line, the quality of the milk on day i appears on the ith line.

    Output

    Line 1: One integer, the length of the longest pattern which occurs at least K times

    Sample Input

    8 2
    1
    2
    3
    2
    3
    2
    3
    1

    Sample Output

    4

    给定一个字符串,求至少出现k次的最长重复子串,这k个子串可以重叠。

    算法分析:这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组。不同的是,这里要判断的是有没有一个组的后缀个数不小于k。如果有,那么存在k个相同的子串满足条件,否则不存在。这个做法的时间复杂度为 O(nlogn)。

    下面给一组数据:5 2  1 1 1 1 1    一组数据发现RE bug

    先贴个RE代码:

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include<string.h>
      4 
      5 using namespace std;
      6 
      7 #define maxn 1100000
      8 
      9 #define cls(x) memset(x, 0, sizeof(x))
     10 
     11 int wa[maxn],wb[maxn],wv[maxn],wss[maxn];
     12 
     13 int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
     14 
     15 //倍增算法
     16 void da(int *r,int *sa,int n,int m)
     17 {
     18      cls(wa);
     19      cls(wb);
     20      cls(wv);
     21      int i,j,p,*x=wa,*y=wb,*t;
     22 
     23      //基数排序
     24      for(i=0;i<m;i++) wss[i]=0;
     25      for(i=0;i<n;i++) wss[x[i]=r[i]]++;
     26      for(i=1;i<m;i++) wss[i]+=wss[i-1];
     27      for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
     28 
     29      // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。
     30      for(j=1,p=1;p<n;j*=2,m=p)
     31      {
     32        //接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次
     33        for(p=0,i=n-j;i<n;i++) y[p++]=i;
     34        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
     35 
     36        //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序,
     37        for(i=0;i<n;i++) wv[i]=x[y[i]];
     38        for(i=0;i<m;i++) wss[i]=0;
     39        for(i=0;i<n;i++) wss[wv[i]]++;
     40        for(i=1;i<m;i++) wss[i]+=wss[i-1];
     41        for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
     42 
     43        //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。
     44        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
     45        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     46      }
     47 }
     48 
     49 int rank[maxn],height[maxn];
     50 
     51 //得到height数组:排名相邻的两个后缀的最长公共前缀
     52 void calheight(int *r,int *sa,int n)
     53 {
     54      cls(rank);
     55      cls(height);
     56      int i,j,k=0;
     57      for(i=1;i<n;i++) rank[sa[i]]=i;
     58      for(i=0;i<n;height[rank[i++]]=k)
     59      for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
     60      return;
     61 }
     62     
     63 int ca[maxn];
     64 int sa[maxn];
     65 int N;
     66 
     67 int isok(int m,int k){
     68     int sum=0,i;
     69     for(i=1;i<N;i++){
     70         if(height[i]>=m){
     71             sum++;
     72             if(sum>=k-1){
     73                 return 1;
     74             }
     75         }else{
     76             sum=0;
     77         }
     78     }
     79     return 0;
     80 }
     81 
     82 int main()
     83 {
     84     int k,i;
     85     while (~scanf("%d%d",&N,&k))
     86     {
     87         for(i=0;i<N;i++)
     88             scanf("%d",&ca[i]);
     89         da(ca, sa, N, maxn);
     90         calheight(ca,sa,N);
     91 
     92      //  for(i=0;i<N;i++)  printf("%d ",height[i]); putchar(10);
     93         int j;
     94         i=0;
     95         j=N;
     96         while(i<=j){
     97             int mid = (i+j)/2;
     98             if(isok(mid,k)){
     99                 i=mid+1;
    100             }else{
    101                 j=mid-1;
    102             }
    103         }
    104         printf("%d
    ",j);
    105     }
    106     return 0;
    107 }
    View Code

    再来一个AC码:(根据RE代码改过来的)

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include<string.h>
      4 
      5 using namespace std;
      6 
      7 #define maxn 1100000
      8 
      9 #define cls(x) memset(x, 0, sizeof(x))
     10 
     11 int wa[maxn],wb[maxn],wv[maxn],wss[maxn];
     12 
     13 int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
     14 
     15 //倍增算法
     16 void da(int *r,int *sa,int n,int m)
     17 {
     18      cls(wa);
     19      cls(wb);
     20      cls(wv);
     21      int i,j,p,*x=wa,*y=wb,*t;
     22 
     23      //基数排序
     24      for(i=0;i<m;i++) wss[i]=0;
     25      for(i=0;i<n;i++) wss[x[i]=r[i]]++;
     26      for(i=1;i<m;i++) wss[i]+=wss[i-1];
     27      for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
     28 
     29      // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。
     30      for(j=1,p=1;p<n;j*=2,m=p)
     31      {
     32        //接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次
     33        for(p=0,i=n-j;i<n;i++) y[p++]=i;
     34        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
     35 
     36        //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序,
     37        for(i=0;i<n;i++) wv[i]=x[y[i]];
     38        for(i=0;i<m;i++) wss[i]=0;
     39        for(i=0;i<n;i++) wss[wv[i]]++;
     40        for(i=1;i<m;i++) wss[i]+=wss[i-1];
     41        for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
     42 
     43        //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。
     44        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
     45        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     46      }
     47 }
     48 
     49 int rank[maxn],height[maxn];
     50 
     51 //得到height数组:排名相邻的两个后缀的最长公共前缀
     52 void calheight(int *r,int *sa,int n)
     53 {
     54      cls(rank);
     55      cls(height);
     56      int i,j,k=0;
     57      
     58      for(i=1;i<n;i++) rank[sa[i]]=i;
     59     // for(i=0;i<n;i++)  printf("%d ",rank[i]); putchar(10); system("pause");
     60 
     61      for(i=0;i<n;height[rank[i++]]=k)
     62          for(k?k--:0,j=sa[((rank[i]-1<0)?0:rank[i]-1)];r[i+k]==r[j+k]&&i!=j;k++);   // 加上r[i+k]==r[j+k] => r[i+k]==r[j+k]&&i!=j
     63      return;
     64 }
     65     
     66 int ca[maxn];
     67 int sa[maxn];
     68 int N;
     69 
     70 int isok(int m,int k){
     71     int sum=0,i;
     72     for(i=1;i<N;i++){
     73         if(height[i]>=m){
     74             sum++;
     75             if(sum>=k-1){
     76                 return 1;
     77             }
     78         }else{
     79             sum=0;
     80         }
     81     }
     82     return 0;
     83 }
     84 
     85 int main()
     86 {
     87     int k,i;
     88     while (~scanf("%d%d",&N,&k))
     89     {
     90         for(i=0;i<N;i++)
     91             scanf("%d",&ca[i]);
     92         ca[N] = 1000000;   //加上一个结尾,排除数字全相同的情况
     93         N++;
     94         da(ca, sa, N, maxn-1);
     95         calheight(ca,sa,N);
     96 
     97         int j;
     98         i=0;
     99         j=N;
    100         while(i<=j){
    101             int mid = (i+j)/2;
    102             if(isok(mid,k)){
    103                 i=mid+1;
    104             }else{
    105                 j=mid-1;
    106             }
    107         }
    108         printf("%d
    ",j);
    109     }
    110     return 0;
    111 }


    后缀数组第二种实现方式:AC码:

      1 #include<iostream>
      2 #include<stdio.h>
      3 #include<string.h>
      4 #define MAXN 500010
      5 
      6 char str[MAXN*2];
      7 int s[MAXN*2],sa[MAXN*2],rank[MAXN*2],trank[MAXN*4],sum[MAXN*4],tsa[MAXN*2],height[MAXN*2];
      8 
      9 void sorting(int j,int len)//基数排序
     10 {
     11     int i;
     12     memset(sum,0,sizeof(sum));
     13     for (i=1; i<=len; i++) sum[ rank[i+j] ]++;
     14     for (i=1; i<=len; i++) sum[i]+=sum[i-1];
     15     for (i=len; i>0; i--) tsa[ sum[ rank[i+j] ]-- ]=i;//对第二关键字计数排序,tsa代替sa为排名为i的后缀是tsa[i]
     16 
     17     memset(sum,0,sizeof(sum));
     18     for (i=1; i<=len; i++) sum[ rank[i] ]++;
     19     for (i=1; i<=len; i++) sum[i]+=sum[i-1];
     20     for (i=len; i>0; i--) sa[ sum[ rank[ tsa[i] ] ]-- ]= tsa[i]; //对第一关键字计数排序
     21     //构造互逆关系
     22 //    for(i=1;i<=len;i++) printf("%d ",rank[i]); putchar(10);
     23 }
     24 
     25 void getsa(int len){
     26 
     27      memset(sum,0,sizeof(sum));
     28      memset(rank,0,sizeof(rank));
     29      memset(height,0,sizeof(height));
     30      memset(trank,0,sizeof(trank));
     31      memset(sa,0,sizeof(sa));
     32     memset(tsa,0,sizeof(tsa));
     33 
     34     int i;
     35     for (i=0; i<len; i++) {
     36         trank[i+1]=s[i];
     37     }
     38     for (i=1; i<=len; i++) {
     39         sum[ trank[i] ]++;
     40     }
     41     for (i=1; i<=40; i++) sum[i]+=sum[i-1];
     42     for (i=len; i>0; i--) sa[ sum[ trank[i] ]-- ]=i;
     43 
     44     rank[ sa[1] ]=1;
     45 
     46     int p;
     47     for (i=2,p=1; i<=len; i++)
     48     {
     49         if (trank[ sa[i] ]!=trank[ sa[i-1] ]) p++;
     50         rank[ sa[i] ]=p;
     51     }//第一次的sa与rank构造完成
     52 
     53     for (int j=1; j<=len; j*=2)
     54     {
     55         sorting(j,len);
     56         trank[ sa[1] ]=1; 
     57         p=1; //用trank代替rank
     58         for (i=2; i<=len; i++)
     59         {
     60             if ((rank[ sa[i] ]!=rank[ sa[i-1] ]) || (rank[ sa[i]+j ]!=rank[ sa[i-1]+j ])) p++;
     61             trank[ sa[i] ]=p;//空间要开大一点,至少2倍
     62         }
     63         for (i=1; i<=len; i++) rank[i]=trank[i];
     64     }
     65 }
     66 
     67 void getheight(int len)
     68 {
     69     for (int i=1,j=0; i<=len; i++)//用j代替上面的h数组
     70     {
     71         if (rank[i]==1) continue;
     72         for (; s[i+j-1]==s[ sa[ rank[i]-1 ]+j-1 ]; ) j++;//注意越界之类的问题
     73         height[ rank[i] ]=j;
     74         if (j>0) j--;
     75     }
     76 }
     77 int N;
     78 
     79 int isok(int m,int k){
     80     int sum=0,i;
     81     for(i=2;i<=N;i++){
     82         if(height[i]>=m){
     83             sum++;
     84             if(sum>=k-1){
     85                 return 1;
     86             }
     87         }else{
     88             sum=0;
     89         }
     90     }
     91     return 0;
     92 }
     93 
     94 int main()
     95 {
     96     int k,i;
     97     while (~scanf("%d%d",&N,&k))
     98     {
     99         for(i=0;i<N;i++)
    100             scanf("%d",&s[i]);
    101         getsa(N);
    102         getheight(N);
    103 
    104         int j;
    105         i=0;
    106         j=N;
    107         while(i<=j){
    108             int mid = (i+j)/2;
    109             if(isok(mid,k)){
    110                 i=mid+1;
    111             }else{
    112                 j=mid-1;
    113             }
    114         }
    115         printf("%d
    ",j);
    116     }
    117     return 0;
    118 }

     

  • 相关阅读:
    MFC——9.多线程与线程同步
    hdu 1598 find the most comfortable road(并查集+枚举)
    POJ3107Godfather[树形DP 树的重心]
    Codeforces 410C.Team[构造]
    Codeforces 715A. Plus and Square Root[数学构造]
    BZOJ1015[JSOI2008]星球大战starwar[并查集]
    洛谷U4727小L的二叉树[树转序列 LIS]
    Codeforces 500B. New Year Permutation[连通性]
    Codeforces 549D. Hear Features[贪心 英语]
    Codeforces 549C. The Game Of Parity[博弈论]
  • 原文地址:https://www.cnblogs.com/crazyapple/p/3200037.html
Copyright © 2020-2023  润新知