后缀数组练习2:可重叠的k次最长重复子串

其实和上一题是差不多的,只是在二分check的时候有一些小小的改动

1468: 后缀数组2:可重叠的k次最长重复子串

poj3261

时间限制: 1 Sec  内存限制: 128 MB
提交: 113  解决: 48
[提交] [状态] [讨论版] [命题人:admin]
题目描述

【问题描述】

农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天 产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个0到1000000之间的数。并且John记录了N(1<=N<=20000)天的 牛奶质量值。他想知道最长的出现了至少K(2<=K<=N)次的模式的长度。 比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当K=2时,这个长度为4。(可重叠的k次最长重复子串)

【输入格式】

* Line 1: 两个整数 N,K。

* Lines 2..N+1: 每行一个整数表示当天的质量值。

(多组数据)

【输出格式】

* Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度

【样例】
输入:

8 2

1

2

3

2

3

2

3

1
输出:

做法是是和上一题:不可重叠的最长重复子串是差不多的,就是要判断后缀分成若干组,然后判断有没有一个组后缀个数是不小于k的,存在就满足,不存在就不满足
代码实现,没什么注释,所以我就只放一个版本
 1 /*后将后缀分成若干组。 不同的是,这里要判断的是有没有一个组的后缀个数不小于 k。
 2 如果有,那么存在k 个相同的子串满足条件,否则不存在*/
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<iostream>
 9 using namespace std;
10 int sa[1000010],Rank[1000010],rsort[1000010];
11 int a[1000010],cnt[1000010],pos[100010],height[1000010];
12 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
13 void get_sa(int n,int m) 
14 {
15     int k=1,p=0,len;
16     for(int i=1;i<=n;i++) Rank[i]=a[i];
17     memset(rsort,0,sizeof(rsort));
18     for(int i=1;i<=n;i++) rsort[Rank[i]]++;
19     for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
20     for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i;
21     for(int k=1;k<n;k<<=1)
22     {
23         len=0;
24         for(int i=n-k+1;i<=n;i++) pos[++len]=i;
25         for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
26         for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]];
27         memset(rsort,0,sizeof(rsort));
28         for(int i=1;i<=n;i++) rsort[cnt[i]]++;
29         for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
30         for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i];
31         for(int i=1;i<=n;i++) cnt[i]=Rank[i];
32         p=1; Rank[sa[1]]=1;
33         for(int i=2;i<=n;i++)
34         {
35             if(!cmp(sa[i],sa[i-1],k)) p++;
36             Rank[sa[i]]=p;
37         }
38         if(p==n) break; m=p;
39     }
40     a[0]=0; sa[0]=0;
41 }
42 void get_he(int n)
43 {
44     int j,k=0;
45     for(int i=1;i<=n;i++)
46     {
47         j=sa[Rank[i]-1];
48         if(k) k--;
49         while(a[j+k]==a[i+k]) k++;
50         height[Rank[i]]=k;
51     }
52 }
53 bool check(int mid,int n,int k)
54 {
55     int sum=1;
56     for(int i=2;i<=n;i++)
57     {
58         if(height[i]>=mid)/*满足条件*/
59         {
60             sum++;/*个数增加*/
61             if(sum==k) return true;/*如果满足条件就直接返回*/
62         }
63         else sum=1;/*否则重新找*/
64     }
65     return false;
66 }
67 int main() 
68 {
69     int n,k;
70     while(scanf("%d%d",&n,&k)!=EOF)
71     {
72         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
73         get_sa(n,1000010); get_he(n);
74         int l=0,r=n,ans=0;
75         while(l<=r)
76         {
77             int mid=(l+r)/2;
78             if(check(mid,n,k))
79             {
80                 ans=mid;
81                 l=mid+1;
82             }
83             else r=mid-1;
84         }
85         printf("%d\n",ans);
86     }
87     return 0;
88 }
Tristan Code
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值