zoukankan      html  css  js  c++  java
  • 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,没写

  • 相关阅读:
    微信小程序开发之http到https的转化
    微信公众号实践第一弹:群发功能
    C# Modbus_TCP通讯 dll库 测试TRIO运动控制器
    C语言----循环结构2(基础篇六)
    C# 调用TRIO控制器ActiveX教程
    C语言----循环结构1(基础篇五)
    TRIO-basic指令--CAM
    C语言----流程图(基础篇四)
    C语言----选择结构(基础篇三)
    C语言----输入输出语句(基础篇二)
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12213534.html
Copyright © 2011-2022 走看看