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

时间:2022-08-29 19:00:26
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代码:

 #include <iostream>
#include <stdio.h>
#include<string.h> using namespace std; #define maxn 1100000 #define cls(x) memset(x, 0, sizeof(x)) int wa[maxn],wb[maxn],wv[maxn],wss[maxn]; int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} //倍增算法
void da(int *r,int *sa,int n,int m)
{
cls(wa);
cls(wb);
cls(wv);
int i,j,p,*x=wa,*y=wb,*t; //基数排序
for(i=;i<m;i++) wss[i]=;
for(i=;i<n;i++) wss[x[i]=r[i]]++;
for(i=;i<m;i++) wss[i]+=wss[i-];
for(i=n-;i>=;i--) sa[--wss[x[i]]]=i; // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。
for(j=,p=;p<n;j*=,m=p)
{
//接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次
for(p=,i=n-j;i<n;i++) y[p++]=i;
for(i=;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序,
for(i=;i<n;i++) wv[i]=x[y[i]];
for(i=;i<m;i++) wss[i]=;
for(i=;i<n;i++) wss[wv[i]]++;
for(i=;i<m;i++) wss[i]+=wss[i-];
for(i=n-;i>=;i--) sa[--wss[wv[i]]]=y[i]; //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。
for(t=x,x=y,y=t,p=,x[sa[]]=,i=;i<n;i++)
x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
}
} int rank[maxn],height[maxn]; //得到height数组:排名相邻的两个后缀的最长公共前缀
void calheight(int *r,int *sa,int n)
{
cls(rank);
cls(height);
int i,j,k=;
for(i=;i<n;i++) rank[sa[i]]=i;
for(i=;i<n;height[rank[i++]]=k)
for(k?k--:,j=sa[rank[i]-];r[i+k]==r[j+k];k++);
return;
} int ca[maxn];
int sa[maxn];
int N; int isok(int m,int k){
int sum=,i;
for(i=;i<N;i++){
if(height[i]>=m){
sum++;
if(sum>=k-){
return ;
}
}else{
sum=;
}
}
return ;
} int main()
{
int k,i;
while (~scanf("%d%d",&N,&k))
{
for(i=;i<N;i++)
scanf("%d",&ca[i]);
da(ca, sa, N, maxn);
calheight(ca,sa,N); // for(i=0;i<N;i++) printf("%d ",height[i]); putchar(10);
int j;
i=;
j=N;
while(i<=j){
int mid = (i+j)/;
if(isok(mid,k)){
i=mid+;
}else{
j=mid-;
}
}
printf("%d\n",j);
}
return ;
}

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

 #include <iostream>
#include <stdio.h>
#include<string.h> using namespace std; #define maxn 1100000 #define cls(x) memset(x, 0, sizeof(x)) int wa[maxn],wb[maxn],wv[maxn],wss[maxn]; int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} //倍增算法
void da(int *r,int *sa,int n,int m)
{
cls(wa);
cls(wb);
cls(wv);
int i,j,p,*x=wa,*y=wb,*t; //基数排序
for(i=;i<m;i++) wss[i]=;
for(i=;i<n;i++) wss[x[i]=r[i]]++;
for(i=;i<m;i++) wss[i]+=wss[i-];
for(i=n-;i>=;i--) sa[--wss[x[i]]]=i; // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。
for(j=,p=;p<n;j*=,m=p)
{
//接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次
for(p=,i=n-j;i<n;i++) y[p++]=i;
for(i=;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序,
for(i=;i<n;i++) wv[i]=x[y[i]];
for(i=;i<m;i++) wss[i]=;
for(i=;i<n;i++) wss[wv[i]]++;
for(i=;i<m;i++) wss[i]+=wss[i-];
for(i=n-;i>=;i--) sa[--wss[wv[i]]]=y[i]; //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。
for(t=x,x=y,y=t,p=,x[sa[]]=,i=;i<n;i++)
x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
}
} int rank[maxn],height[maxn]; //得到height数组:排名相邻的两个后缀的最长公共前缀
void calheight(int *r,int *sa,int n)
{
cls(rank);
cls(height);
int i,j,k=; for(i=;i<n;i++) rank[sa[i]]=i;
// for(i=0;i<n;i++) printf("%d ",rank[i]); putchar(10); system("pause"); for(i=;i<n;height[rank[i++]]=k)
for(k?k--:,j=sa[((rank[i]-<)?:rank[i]-)];r[i+k]==r[j+k]&&i!=j;k++); // 加上r[i+k]==r[j+k] => r[i+k]==r[j+k]&&i!=j
return;
} int ca[maxn];
int sa[maxn];
int N; int isok(int m,int k){
int sum=,i;
for(i=;i<N;i++){
if(height[i]>=m){
sum++;
if(sum>=k-){
return ;
}
}else{
sum=;
}
}
return ;
} int main()
{
int k,i;
while (~scanf("%d%d",&N,&k))
{
for(i=;i<N;i++)
scanf("%d",&ca[i]);
ca[N] = ; //加上一个结尾,排除数字全相同的情况
N++;
da(ca, sa, N, maxn-);
calheight(ca,sa,N); int j;
i=;
j=N;
while(i<=j){
int mid = (i+j)/;
if(isok(mid,k)){
i=mid+;
}else{
j=mid-;
}
}
printf("%d\n",j);
}
return ;
}

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

 #include<iostream>
#include<stdio.h>
#include<string.h>
#define MAXN 500010 char str[MAXN*];
int s[MAXN*],sa[MAXN*],rank[MAXN*],trank[MAXN*],sum[MAXN*],tsa[MAXN*],height[MAXN*]; void sorting(int j,int len)//基数排序
{
int i;
memset(sum,,sizeof(sum));
for (i=; i<=len; i++) sum[ rank[i+j] ]++;
for (i=; i<=len; i++) sum[i]+=sum[i-];
for (i=len; i>; i--) tsa[ sum[ rank[i+j] ]-- ]=i;//对第二关键字计数排序,tsa代替sa为排名为i的后缀是tsa[i] memset(sum,,sizeof(sum));
for (i=; i<=len; i++) sum[ rank[i] ]++;
for (i=; i<=len; i++) sum[i]+=sum[i-];
for (i=len; i>; i--) sa[ sum[ rank[ tsa[i] ] ]-- ]= tsa[i]; //对第一关键字计数排序
//构造互逆关系
// for(i=1;i<=len;i++) printf("%d ",rank[i]); putchar(10);
} void getsa(int len){ memset(sum,,sizeof(sum));
memset(rank,,sizeof(rank));
memset(height,,sizeof(height));
memset(trank,,sizeof(trank));
memset(sa,,sizeof(sa));
memset(tsa,,sizeof(tsa)); int i;
for (i=; i<len; i++) {
trank[i+]=s[i];
}
for (i=; i<=len; i++) {
sum[ trank[i] ]++;
}
for (i=; i<=; i++) sum[i]+=sum[i-];
for (i=len; i>; i--) sa[ sum[ trank[i] ]-- ]=i; rank[ sa[] ]=; int p;
for (i=,p=; i<=len; i++)
{
if (trank[ sa[i] ]!=trank[ sa[i-] ]) p++;
rank[ sa[i] ]=p;
}//第一次的sa与rank构造完成 for (int j=; j<=len; j*=)
{
sorting(j,len);
trank[ sa[] ]=;
p=; //用trank代替rank
for (i=; i<=len; i++)
{
if ((rank[ sa[i] ]!=rank[ sa[i-] ]) || (rank[ sa[i]+j ]!=rank[ sa[i-]+j ])) p++;
trank[ sa[i] ]=p;//空间要开大一点,至少2倍
}
for (i=; i<=len; i++) rank[i]=trank[i];
}
} void getheight(int len)
{
for (int i=,j=; i<=len; i++)//用j代替上面的h数组
{
if (rank[i]==) continue;
for (; s[i+j-]==s[ sa[ rank[i]- ]+j- ]; ) j++;//注意越界之类的问题
height[ rank[i] ]=j;
if (j>) j--;
}
}
int N; int isok(int m,int k){
int sum=,i;
for(i=;i<=N;i++){
if(height[i]>=m){
sum++;
if(sum>=k-){
return ;
}
}else{
sum=;
}
}
return ;
} int main()
{
int k,i;
while (~scanf("%d%d",&N,&k))
{
for(i=;i<N;i++)
scanf("%d",&s[i]);
getsa(N);
getheight(N); int j;
i=;
j=N;
while(i<=j){
int mid = (i+j)/;
if(isok(mid,k)){
i=mid+;
}else{
j=mid-;
}
}
printf("%d\n",j);
}
return ;
}

poj 3261 Milk Patterns(后缀数组)(k次的最长重复子串)的更多相关文章

  1. Poj 3261 Milk Patterns&lpar;后缀数组&plus;二分答案&rpar;

    Milk Patterns Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk g ...

  2. poj 3261 Milk Patterns 后缀数组 &plus; 二分

    题目链接 题目描述 给定一个字符串,求至少出现 \(k\) 次的最长重复子串,这 \(k\) 个子串可以重叠. 思路 二分 子串长度,据其将 \(h\) 数组 分组,判断是否存在一组其大小 \(\ge ...

  3. POJ 3261 Milk Patterns 后缀数组求 一个串种 最长可重复子串重复至少k次

    Milk Patterns   Description Farmer John has noticed that the quality of milk given by his cows varie ...

  4. POJ 3261 Milk Patterns &lpar; 后缀数组 &amp&semi;&amp&semi; 出现k次最长可重叠子串长度 &rpar;

    题意 : 给出一个长度为 N 的序列,再给出一个 K 要求求出出现了至少 K 次的最长可重叠子串的长度 分析 : 后缀数组套路题,思路是二分长度再对于每一个长度进行判断,判断过程就是对于 Height ...

  5. POJ 3261 Milk Patterns&lpar;后缀数组&plus;单调队列&rpar;

    题意 找出出现k次的可重叠的最长子串的长度 题解 用后缀数组. 然后求出heigth数组. 跑单调队列就行了.找出每k个数中最小的数的最大值.就是个滑动窗口啊 (不知道为什么有人写二分,其实写啥都差不 ...

  6. POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)&plus;后缀数组模板

    Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7586   Accepted: 3448 Cas ...

  7. 后缀数组 POJ 3261 Milk Patterns

    题目链接 题意:可重叠的 k 次最长重复子串.给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠. 分析:与POJ 1743做法类似,先二分答案,height数组分段后统计 LC ...

  8. POJ 3261 Milk Patterns(后缀数组&plus;二分答案&plus;离散化)

    题意:给定一个字符串,求至少出现k 次的最长重复子串,这k 个子串可以重叠. 分析:经典的后缀数组求解题:先二分答案,然后将后缀分成若干组.这里要判断的是有没有一个组的符合要求的后缀个数(height ...

  9. 利用后缀数组&lpar;suffix array&rpar;求最长公共子串&lpar;longest common substring&rpar;

    摘要:本文讨论了最长公共子串的的相关算法的时间复杂度,然后在后缀数组的基础上提出了一个时间复杂度为o(n^2*logn),空间复杂度为o(n)的算法.该算法虽然不及动态规划和后缀树算法的复杂度低,但其 ...

随机推荐

  1. 识别 Linux上的设备(磁盘)类型

    1. Linux 上的设备 (device) Linux 操作系统中,各种设备驱动(device driver)通过设备控制器(device controller)来管理各种设备(device),其关 ...

  2. DataTable select根据条件取值

    1.封装独立方法 // 执行DataTable中的查询返回新的DataTable /// </summary> /// <param name="dt">源 ...

  3. 关于js单页面实现跳转原理以及利用angularjs框架路由实现单页面跳转

    还记得我们刚开始学习html时使用的锚节点实现跳转吗? <a href="#target">我想跳转至目标位置</a> <p>第一条</p ...

  4. iOS:使用MVC模式帮ViewController瘦身

    如何给UIViewController瘦身 随着程序逻辑复杂度的提高,你是否也发现了App中一些ViewController的代码行数急剧增多,达到了2,3千行,甚至更多.这时如果想再添加一点功能或者 ...

  5. install Active Directory域控制器

    设置Active Directory域控制器 正如我们在网络与系统配置专题文章中所提到的那样,我们已将两部服务器设置为对应于内部域“intdomain.com”的Active Directory域控制 ...

  6. &lbrack;redis&rsqb; &lt&semi;&lt&semi;The little Redis book&gt&semi;&gt&semi;的读书笔记

    <<The Little Redis Book>> 请右键点击在新窗口打开,可按原始大小查看.

  7. bzoj千题计划306:bzoj2342&colon; &lbrack;Shoi2011&rsqb;双倍回文 (回文自动机)

    https://www.lydsy.com/JudgeOnline/problem.php?id=2342 解法一: 对原串构建回文自动机 抽离fail树,从根开始dfs 设len[x]表示节点x表示 ...

  8. 简单说明一下Token ,Cookie,Session

    在Web应用中,HTTP请求是无状态的.即:用户第一次发起请求,与服务器建立连接并登录成功后,为了避免每次打开一个页面都需要登录一下,就出现了cookie,Session. Cookie Cookie ...

  9. adb安装启动Touch校正软件

    /********************************************************************************* * adb安装启动Touch校正软 ...

  10. HDU 4745 Two Rabbits 区间dp&lowbar;回文序列

    题目链接: http://blog.csdn.net/scnu_jiechao/article/details/11759333 Two Rabbits Time Limit: 10000/5000 ...