POJ Milk Patterns(后缀数组和二分)

这个题和昨天写的Musical Theme基本一样,那篇博客有后缀数组的学习,如果那个题没有写过的话,搞完了这个题可以去做一下。

题意

显然就是求最长可重复子串的长度,但是该子串重复的次数需不小于K。

思路

罗穗骞 《后缀数组——处理字符串的有力工具》
先求后缀树组,而后利用后缀树组求height数组(height[i]:存储排名为i和i-1的后缀子串的最长重复前缀子串长度 )。把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。例如,字符串为“aabaaaab”,当k=2时,后缀分成了4组如图所示
可以想到当一组内的height都不小于k时,我们只需判断组内包含的后缀数组的数量是否不小于K个就行了,如果满足,那么当长度等于k时也是满足条件的,因此我们需要对长度进行二分,然后判断是否可行即可

代码
#pragma GCC optimize(2)
//#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
#define pb push_back
#define mk make_pair
#define fir first
#define sec second
#define scf scanf
#define prf printf
typedef pair<ll,ll> pa;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll maxn=2e4+7;
const ll maxnum=1e6+7;
ll N,M,K;
ll sa[maxn],rank[maxn],r[maxn],height[maxn],tmp[maxn]; 
bool cmp(ll i,ll j){
    
    
	if(rank[i]!=rank[j])
	return rank[i]<rank[j];
	ll r1=i+K<=N?rank[i+K]:-1;
	ll r2=j+K<=N?rank[j+K]:-1;
	return r1<r2;
}
//求后缀数组 
void do_sa(){
    
    
	ll i,j;
	for(i=0;i<=N;i++){
    
    
		sa[i]=i;
		rank[sa[i]]=(i!=N?r[i]:-1);
	}
	for(K=1;K<=N;K<<=1){
    
    
		sort(sa,sa+1+N,cmp);
		tmp[sa[0]]=0;
		for(i=1;i<=N;i++)
		tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
		for(i=0;i<=N;i++)
		rank[i]=tmp[i];
	}
	return ;
}
void get_height(){
    
    
	ll i,j,k=0;
	//枚举每个后缀的起始位置 
	for(i=0;i<N;i++){
    
    
		if(k)
		k--;
		else
		k=0;
		j=sa[rank[i]-1];
		while(r[i+k]==r[j+k])
		k++;
		height[rank[i]]=k;//height[i]:存储排名为i和i-1的后缀子串的最长前缀子串长度 
	}
	return ;
	
}
bool check(ll mid){
    
    
	ll i,j,cnt=0;
	for(i=1;i<=N;i++){
    
    
		if(height[i]>=mid){
    
    
			cnt++;
			if(cnt+1>=M)
			return true;
		}
		else
		cnt=0;
	}
	return false;
}
int main()
{
    
    
//  freopen(".../.txt","w",stdout);
//  freopen(".../.txt","r",stdin);
	ios::sync_with_stdio(false);
	ll i,j,k;
	cin>>N>>M;
	for(i=0;i<N;i++)
	cin>>r[i];
	do_sa();
	get_height();
	ll L=0,R=N,mid,res=0;
	while(L<=R){
    
    
		mid=(R-L)/2+L;
		if(check(mid)){
    
    
			L=mid+1;
			res=max(res,mid);
		} 
		else{
    
    
			R=mid-1;
		}
	}
	cout<<res<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43311695/article/details/107656420