poj3261 Milk Patterns 后缀数组

继续学习《后缀数组--处理字符串的有力工具》例4:可重叠的 k 次最长重复子串

题目大意:奶牛不好好产奶,农民很受伤,不知道到底肿么回事。然后统计了一大堆数据,现在农民想从这一堆数据里面分析出来奶牛产奶的模式

正题:给定的数据最多有N<=20000个,数据范围是0-1000000,给定一个k,现在要统计这N个数据里面最少出现k次的最长可重叠子串的长度是多少。

解题思路:首先对数据进行放缩处理,2w个数据最大居然有100w,这是受不住的。处理完之后就构造后缀数组,然后二分法求解

二分法思路:以要查找的长度mid把height数组分成几组,计算每组里面height值大于mid的个数,如果个数大于k。那么就代表至少存在以mid长度的可重复k次的子串,所以继续二分求下去,就能求出最长字串。

//Creat Time: 2013年05月27日 星期一 12时49分56秒
//File  Name: poj3261.cpp
//--Author--: GreedyDaam
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")//设置栈大小
using namespace std;
#define MAX 20010 
int wa[MAX],wb[MAX],wv[MAX],ws[MAX];
int rank[MAX],height[MAX],str[MAX],sa[MAX];
struct Milk{
	int q,id;
};
Milk milk[MAX];
bool 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){
	int i,j,p,*x=wa,*y=wb,*t;
	for(i=0;i<m;i++)ws[i]=0;
	for(i=0;i<n;i++)ws[x[i]=r[i]]++;
	for(i=1;i<m;i++)ws[i]+=ws[i-1];
	for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
	for(j=1,p=1;p<n;j*=2,m=p){
		for(p=0,i=n-j;i<n;i++)y[p++]=i;
		for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
		for(i=0;i<n;i++)wv[i]=x[y[i]];
		for(i=0;i<m;i++)ws[i]=0;
		for(i=0;i<n;i++)ws[wv[i]]++;
		for(i=1;i<m;i++)ws[i]+=ws[i-1];
		for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
		for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void cal(int *r,int *sa,int n){
	int i,j,k=0;
	for(i=1;i<=n;i++)rank[sa[i]]=i;
	for(i=0;i<n;height[rank[i++]]=k)
		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void binary(int *sa,int *h,int n,int k){
	int left=0,right=n,mid,i,flag,count;//count记录sa数组中长度大于mid的个数
	while(left<=right){
		mid=(left+right)/2;
		flag=0;count=1;
//初始化count为1,因为计数count的时候我们比较的是height[i]和mid,如果height[i]都大于等于mid,那么对应的sa[i],sa[i-1]一定大于mid.如果一个分组中有两个height值大于mid,那么这个分组中一定是有3个长度大于mid的后缀。所以count初始化为1
		for(i=2;i<=n&&!flag;i++){
			if(h[i]<mid)count=1;
			else{
				count++;
				if(count>=k)flag=1;//如果count大于k了就标记找到
			}
		}
		if(flag)left=mid+1;
		else right=mid-1;
	}
	printf("%d\n",right);
}
bool MilkCmp(Milk m1,Milk m2){
	return m1.q<m2.q;
}
int main(){
	freopen("input.txt","r",stdin);
	int n,i,k,m;
	while(scanf("%d%d",&n,&k)!=EOF){
		for(i=0;i<n;i++){
			scanf("%d",&milk[i].q);
			milk[i].id=i;
		}
		sort(milk,milk+n,MilkCmp);
		str[milk[0].id]=m=1;
		for(i=1;i<n;i++){
			if(milk[i].q!=milk[i-1].q)m++;
			str[milk[i].id]=m;
		}
		str[n]=0;
		da(str,sa,n+1,m+1);
		cal(str,sa,n);
		binary(sa,height,n,k);
	}
return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值