• Luogu2852 [USACO06DEC]牛奶模式Milk Patterns(后缀系列)


    Luogu-2852 [USACO06DEC]牛奶模式Milk Patterns (后缀系列)

    题意:求至少出现了\(k\)次的最长子串

    后缀数组

    二分长度\(x\)

    我们可以将\(LCP\)数组分段,每段内的\(LCP[i]\ge x\),那么出现这些后缀长度为\(x\)的前缀均相同,然后统计每段最多包含几个后缀即可

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cctype>
    #include<cstring>
    using namespace std;
    
    #define reg register
    typedef long long ll;
    #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
    
    template <class T> inline void cmax(T &a,T b){ if(a<b) a=b; }
    template <class T> inline void cmin(T &a,T b){ if(a>b) a=b; }
    
    
    char IO;
    int rd(){
    	int s=0,f=0;
    	while(!isdigit(IO=getchar())) f|=(IO=='-');
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int N=20010,INF=1e9;
    
    int n,m,s[N],h[1000010],hc;
    int cnt[N],tmp[N],rk[N<<1],sa[N],lcp[N];
    
    void PreMake(){
    	rep(i,1,n) cnt[s[i]]++;
    	rep(i,1,hc) cnt[i]+=cnt[i-1];
    	rep(i,1,n) rk[i]=cnt[s[i]],sa[i]=i;
    	for(int k=1;k<=n;k<<=1) {
    		rep(i,0,n) cnt[i]=0;
    		rep(i,1,n) cnt[rk[i+k]]++;
    		rep(i,1,n) cnt[i]+=cnt[i-1];
    		drep(i,n,1) tmp[cnt[rk[i+k]]--]=i;
    		//
    		rep(i,0,n) cnt[i]=0;
    		rep(i,1,n) cnt[rk[i]]++;
    		rep(i,1,n) cnt[i]+=cnt[i-1];
    		drep(i,n,1) sa[cnt[rk[tmp[i]]]--]=tmp[i];
    		//
    		rep(i,1,n) tmp[sa[i]]=tmp[sa[i-1]]+(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+k]!=rk[sa[i-1]+k]);
    		rep(i,1,n) rk[i]=tmp[i];
    	}
    	int h=0;
    	rep(i,1,n) {
    		int j=sa[rk[i]-1];
    		if(h) h--;
    		while(i+h<=n && j+h<=n && s[i+h]==s[j+h]) h++;
    		lcp[rk[i]-1]=h;
    	}
    }
    
    int Check(int lim){ 
    	rep(i,1,n) {
    		int j=i;
    		while(j<n && lcp[j]>=lim) j++;
    		if(j-i+1>=m) return 1;
    		i=j;
    	}
    	return 0;
    }
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,n) {
    		int x=rd();
    		if(!h[x]) h[x]=++hc;
    		s[i]=h[x];
    	}
    	PreMake();
    	int l=1,r=n/m,res=0;
    	while(l<=r) {
    		int mid=(l+r)>>1;
    		if(Check(mid)) l=mid+1,res=mid;
    		else r=mid-1;
    	}
    	printf("%d\n",res);
    }
    
    
    
    
    

    \[\ \]

    后缀自动机

    直接统计\(endpos\)集的大小就可以了,如果\(|endpos|\ge k\)那么答案加上\(maxlen-minlen+1\)

    由于要开map,没写

  • 相关阅读:
    CodeForces 500C New Year Book Reading
    CodeForces 460B Little Dima and Equation 枚举
    CodeForces 451B Sort the Array
    【jquery】jQuery实现轮播图
    【IDEA】IDEA技巧记录
    【eclipse】日常使用eclipse记录
    【SSM】spring配置文件中读取配置文件的三种方式
    【Git】IDEA克隆和提交项目于码云
    semantic UI—表单验证
    【spring Data Jpa】JPA生成数据库表
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12213534.html
Copyright © 2020-2023  润新知