zoukankan      html  css  js  c++  java
  • 【BZOJ4310】跳蚤

    【BZOJ4310】跳蚤

    Description

      很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。
      首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。
      现在他想找一个最优的分法让“魔力串”字典序最小。

    Input

      从文件flea.in中读入数据。
      第一行一个整数k。
      接下来一个长度不超过10^5的字符串 S。

    Output

      输出到文件flea.out中。
      输出一行,表示字典序最小的“魔力串”。

    Sample Input

    13 bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba

    Sample Output

    cbc

    Hint

    【数据规模和约定】
      对于30%的数据,S的长度<=100
      对于60%的数据,S的长度<=1000
      对于100%的数据,S的长度<=100000

    后缀自动机+二分+Hash。

    看到最大值最小,容易想到二分答案。

    我们将原串建好后缀自动机,这样我们二分的就是字符串的排名。二分出一个(ans)过后,找到具有该排名的串在原串中的位置(这个很好找)。

    然后就贪心验证。从后往前尽量加入字符串,如果不能加入就开一个新的子串。在最前面加入一个字符后相当于加入了一个前缀,所以我们比较的时候用二分+Hash找到第一个与答案串不同的位置,然后比较大小。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 200005
    #define int ll
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int K,n;
    char s[N];
    int mxlen[N<<1],fail[N<<1];
    int ch[N<<1][26];
    int cnt=1,last=1;
    int pos[N<<1];
    void Insert(int f,int i) {
    	static int p,now;
    	now=++cnt;
    	p=last,last=now;
    	pos[now]=i;
    	mxlen[now]=mxlen[p]+1;
    	while(p&&!ch[p][f]) ch[p][f]=now,p=fail[p];
    	if(!p) return fail[now]=1,void();
    	int q=ch[p][f];
    	if(mxlen[q]==mxlen[p]+1) return fail[now]=q,void();
    	int New=++cnt;
    	memcpy(ch[New],ch[q],sizeof(ch[q]));
    	fail[New]=fail[q];
    	fail[q]=fail[now]=New;
    	mxlen[New]=mxlen[p]+1;
    	while(p&&ch[p][f]==q) ch[p][f]=New,p=fail[p];
    }
    
    int t[N<<1],q[N<<1];
    ll size[N<<1];
    void top_sort(int n) {
    	for(int i=1;i<=cnt;i++) t[mxlen[i]]++;
    	for(int i=1;i<=n;i++) t[i]+=t[i-1];
    	for(int i=1;i<=cnt;i++) q[t[mxlen[i]]--]=i;
    	for(int i=cnt;i>=1;i--) {
    		int v=q[i];
    		size[v]=1;
    		for(int j=0;j<26;j++)
    			if(ch[v][j]) size[v]+=size[ch[v][j]];
    		pos[fail[v]]=pos[v];
    	}
    }
    
    const ll p=37,mod=1e9+7;
    ll Hash[N],pw[N];
    int ls,rs;
    int ed,len; 
    
    void Find_kth(int now,ll k) {
    	if(k==1&&now!=1) return ed=pos[now],void();
    	k-=(now!=1);
    	for(int i=0;i<26;i++) {
    		if(!ch[now][i]) continue ;
    		if(size[ch[now][i]]>=k) {
    			len++;
    			Find_kth(ch[now][i],k);
    			return ;
    		}
    		k-=size[ch[now][i]];
    	}
    }
    
    ll Get_hash(int l,int r) {return (Hash[r]-Hash[l-1]*pw[r-l+1]%mod+mod)%mod;}
    int same(int l1,int r1,int l2,int r2) {
    	if(s[l1]!=s[l2]) return 0;
    	int l=1,r=min(r1-l1+1,r2-l2+1),mid;
    	while(l<r) {
    		mid=l+r+1>>1;
    		if(Get_hash(l1,l1+mid-1)==Get_hash(l2,l2+mid-1)) l=mid;
    		else r=mid-1;
    	}
    	return l;
    }
    
    bool low(int l1,int r1,int l2,int r2) {
    	int x=same(l1,r1,l2,r2);
    	if(x==r1-l1+1) return 1;
    	if(x==r2-l2+1) return 0;
    	return s[l1+x]<s[l2+x];
    }
    
    bool solve(int ans) {
    	ed=0,len=0;
    	Find_kth(1,ans);
    	ls=ed-len+1,rs=ed;
    	int k=1;
    	int i,j;
    	for(i=n;i>=1;i--) {
    		for(j=i;j>=1;j--) {
    			if(!low(j,i,ls,rs)) {
    				if(i==j) return 0;
    				k++;
    				break;
    			}
    		}
    		i=j+1;
    	}
    	return k<=K;
    }
    
    void pre() {
    	K=Get();
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	pw[0]=1;
    	for(int i=1;i<=n;i++) pw[i]=pw[i-1]*p%mod;
    	for(int i=1;i<=n;i++) Hash[i]=(Hash[i-1]*p+s[i]-'a'+1)%mod;
    	for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    	top_sort(n); 
    }
    
    main() {
    	pre(); 
    	ll l=1,r=size[1]-1,mid;
    	while(l<r) {
    		mid=l+r>>1;
    		if(solve(mid)) r=mid;
    		else l=mid+1;
    	}
    	ed=0,len=0;
    	Find_kth(1,l);
    	rs=ed,ls=rs-len+1;
    	for(int i=ls;i<=rs;i++) cout<<s[i];
    	return 0;
    	for(int i=1;i<=size[1]-1;i++) {
    		ed=0,len=0;
    		Find_kth(1,i);
    		rs=ed,ls=ed-len+1;
    		for(int i=ls;i<=rs;i++) cout<<s[i];
    		cout<<"
    ";
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    机器学习(04)——常用专业术语
    C# 线程同步的三类情景
    线程同步的情景之三
    线程同步的情景之二
    线程同步的情景之一
    Thread.Sleep(0) vs Sleep(1) vs Yeild
    Visual Studio 实用扩展推荐
    为革命保护视力 --- 给 Visual Studio 换颜色
    免费的精品: Productivity Power Tools 动画演示
    如何扩展 Visual Studio 编辑器
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10094622.html
Copyright © 2011-2022 走看看