• [BZOJ4310]跳蚤


    XXVII.[BZOJ4310]跳蚤

    我们仍然考虑二分子串。设当前二分的子串从位置\(pos\)开始,长度为\(len\)。考虑如何编写check函数。

    一个naive的想法便是从前往后枚举所有极大的不存在小于二分串的子串的段,然后将该段数与规定段数作比较。

    但是这有点问题——我们发现,这样做每次都是为段中所有后缀各添加一个字符,不好快速判断。但我们可以借鉴XIV.[SDOI2016]生成魔咒的思想,反过来倒序枚举极大段,这次每次都只是增加一个前缀了。

    于是只要判断该前缀与二分串的字典序关系,如果字典序大于二分串就切一刀即可。

    因为所有子串数是\(O(n^2)\)的,所以实际复杂度为\(O(n\log n^2)=O(n\times2\log n)=O(n\log n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100100;
    typedef long long ll;
    ll sum[N];
    int K;
    namespace Suffix_Array{
    	int x[N],y[N],buc[N],sa[N],ht[N],rk[N],n,m,LG[N],mn[N][20];
    	char s[N];
    	bool mat(int a,int b,int k){
    		if(y[a]!=y[b])return false;
    		if((a+k<n)^(b+k<n))return false;
    		if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
    		return true;
    	}
    	void SA(){
    		for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
    		for(int k=1;k<n;k<<=1){
    			int num=0;
    			for(int i=n-k;i<n;i++)y[num++]=i;
    			for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
    			for(int i=0;i<=m;i++)buc[i]=0;
    			for(int i=0;i<n;i++)buc[x[y[i]]]++;
    			for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    			for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i];
    			swap(x,y);
    			x[sa[0]]=num=0;
    			for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
    			if(num>=n-1)break;
    			m=num;
    		}
    		for(int i=0;i<n;i++)rk[sa[i]]=i;
    		for(int i=0,k=0;i<n;i++){
    			if(!rk[i])continue;
    			if(k)k--;
    			int j=sa[rk[i]-1];
    			while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
    			ht[rk[i]]=k;
    		}
    		for(int i=2;i<n;i++)LG[i]=LG[i>>1]+1;
    		for(int i=1;i<n;i++)mn[i][0]=ht[i];
    		for(int j=1;j<=LG[n-1];j++)for(int i=1;i+(1<<j)-1<n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
    	}
    	int LCP(int x,int y){
    		if(x==y)return n-x;
    		x=rk[x],y=rk[y];
    		if(x>y)swap(x,y);
    		x++;
    		int k=LG[y-x+1];
    		return min(mn[x][k],mn[y-(1<<k)+1][k]);
    	}
    }
    using namespace Suffix_Array;
    bool che(ll ip){
    	int pos=lower_bound(sum,sum+n,ip)-sum,len=ip-sum[pos-1]+ht[pos];
    	pos=sa[pos];
    //	printf("%lld:%d,%d\n",ip,pos,len);
    	int k=0;
    	for(int i=n-1,j=n-1;i>=0;i=j,k++){//numerate backwards
    		for(;j>=0;j--){
    			int tmp=min({LCP(j,pos),i-j+1,len});//the minimum length possible
    			if(tmp==i-j+1&&tmp<=len)continue;//is ok
    			if(tmp==len)break;
    			if(rk[j]>rk[pos])break;//a greater lexicographicity, break.
    		}
    		if(i==j)return false;//there's a SINGLE CHARACTER that is lexicographically greater than substring checked, so break out.
    	}
    	return k<=K;
    }
    int main(){
    	scanf("%d%s",&K,s),n=strlen(s),m='z',SA();
    //	for(int i=0;i<n;i++)printf("%d ",sa[i]);puts("");
    	sum[0]=n-sa[0];
    	for(int i=1;i<n;i++)sum[i]=sum[i-1]+(n-sa[i])-ht[i];
    //	for(int i=0;i<n;i++)printf("%2d ",i);puts("");
    //	for(int i=0;i<n;i++)printf("%2d ",s[i]-'a');puts(""); 
    //	for(int i=0;i<n;i++)printf("%d ",sum[i]);puts("");
    	ll l=1,r=sum[n-1];
    	while(l<r){
    		ll mid=(l+r)>>1;
    		if(che(mid))r=mid;
    		else l=mid+1;
    	}
    	int pos=lower_bound(sum,sum+n,l)-sum,len=l-sum[pos-1]+ht[pos];
    	for(int i=0;i<len;i++)putchar(s[sa[pos]+i]);
    	return 0;
    } 
    /*
    3
    ihgjhdgajhdjfjddddfdj
    */
    

  • 相关阅读:
    Git代码行数统计命令
    JPA访问数据库的几种方式
    爱码小士丨代码一敲十年,收入虽高前途摇摆
    “肉瘾”女孩从软件测试工程师到主管的成长感悟
    华为测试大牛Python+Django接口自动化怎么写的?
    携程大牛的单元测试是怎么样写的?
    Jmeter参数的AES加密使用
    弄啥嘞?热爱你的Bug
    “进腾讯工作一个月,我想辞职了”
    我在华为,软件测试人员在工作中如何运用Linux?
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605396.html
Copyright © 2020-2023  润新知