繼續學習《後綴數組--處理字符串的有力工具》例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;
}