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(后缀数组+二分答案)

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

  2. poj 3261 Milk Patterns 后缀数组 + 二分

    题目链接 题目描述 给定一个字符串,求至少出现 \(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 ( 后缀数组 && 出现k次最长可重叠子串长度 )

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

  5. POJ 3261 Milk Patterns(后缀数组+单调队列)

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

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

    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(后缀数组+二分答案+离散化)

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

  9. 利用后缀数组(suffix array)求最长公共子串(longest common substring)

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

随机推荐

  1. uiautomator做自动化的过程

    UIautiomator官网地址:http://android.toolib.net/sdk/index.html 1.环境搭建 使用uiautomator需要导入jar包,uiautomator.j ...

  2. jsp的useBean标签使用

    创建JavaBean package com.itheima.domain; public class Person { private String name; private int age; p ...

  3. ###Linux基础 - 1

    点击查看Evernote原文. #@author: gr #@date: 2014-10-11 #@email: forgerui@gmail.com 前言 为什么学习Linux?:-) 酷Cool ...

  4. centos 7下配置mysql+php(ThinkPHP)+nginx

    最近在Linux平台上配置服务器部署网站(说多了都是泪!),记个笔记! 一.首先是在centos下安装mysql (参考博客) mysql yum库提供了一个简单的和方便的方法来安装和更新MySQL相 ...

  5. TextView实现跑马灯效果

    网上有很多跑马灯的介绍,有很多跑马灯的代码.或许我的不是最好的,但是应该很容易明白的. 我们先来介绍一个跑马灯的代码 <LinearLayout xmlns:android="http ...

  6. C语言遍历一个文件夹下面的所有文件

    主要用到的函数/function. These should get you started: opendir() readdir() closedir() fopen() fread() fwrit ...

  7. poj代码搬家啦啦啦

    我的poj代码搬家啦,大家想看可以到 blog.csdn.net/michaelysm 来看.欢迎哦

  8. bzoj1016:[JSOI2008]最小生成树计数

    思路:模拟kruskal的过程,可以发现对于所有权值相同的边,有很多种选择的方案,而且权值不同的边并不会相互影响,因为先考虑权值较小的边,权值比当前权值大的边显然不在考虑范围之内,而权值比当前权值小的 ...

  9. [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体 ...

  10. 九度OJ 1446 Head of a Gang -- 并查集

    题目地址:http://ac.jobdu.com/problem.php?pid=1446 题目描述: One way that the police finds the head of a gang ...