zoukankan      html  css  js  c++  java
  • #4303. 跳蚤

    题意
    内存限制:256 MiB
    时间限制:1000 ms
    很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。

    首先,他会把串分成不超过 $k$ 个子串,然后对于每个子串 $S$,他会从 $S$ 的所有子串中选择字典序最大的那一个,并在选出来的 $k$
    个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最小。

    $k le 15$ , $|S| le 10^5$
    题解
    利用 $SAM/SA$ 求出本质不同串个数,进行二分
    对于 $mid$ ,利用 $SAM/SA$ 求出其对应的子串 $s$
    因为一个串的最大字典序子串一定是其后缀(可以画个图理解一下,所以从后往前枚举,如果 $[i,last]$ 的字典序比 $s$ 大,则必须在 $[i-1,last]$ 分割, $i$ 作为新的一个 $last$
    求字典序只需求出最长公共前缀,可以二分 $hash$ 或者 $SA$ 进行处理
    我写的是 $SAM$ +二分 $hash$ ,效率 $O(nlogn-nlog^2n)$(我也不会分析了

    代码

    #include <bits/stdc++.h>
    #define K 793999
    #define LL long long
    #define U unsigned LL
    using namespace std;
    const int N=2e5+5;
    int k,n,lst=1,sz=1,t[N],e[N],tp;
    LL s[N],f[N];U b[N],h[N][2];
    char str[N],now[N];
    struct SAM{
        int link,len,nx[26];
    }a[N];
    void build(int x){
        int np=++sz,p=lst;
        a[np].len=a[p].len+1;
        while(p && !a[p].nx[x])
            a[p].nx[x]=np,p=a[p].link;
        if (!p) a[np].link=1;
        else{
            int q=a[p].nx[x];
            if (a[q].len==a[p].len+1)
                a[np].link=q;
            else{
                int nq=++sz;
                a[nq]=a[q];
                a[nq].len=a[p].len+1;
                a[q].link=a[np].link=nq;
                while(p && a[p].nx[x]==q)
                    a[p].nx[x]=nq,p=a[p].link;
            }
        }
        lst=np;
    }
    void find(int u,LL v){
        v-=f[u];if (!v) return;
        for (int j,i=0;i<26;i++)
            if ((j=a[u].nx[i])){
                if (v>s[j]) v-=s[j];
                else{
                    now[++tp]=i+'a',find(j,v);
                    return;
                }
            }
    }
    U H(int l,int r,bool ty){
        return h[r][ty]-h[l-1][ty]*b[r-l+1];
    }
    bool cmp(int L,int R){
        int d,l=0,r=min(tp,R-L+1);
        while(l<r){
            d=(l+r+1)>>1;
            if (H(1,d,1)==H(L,L+d-1,0))
                l=d;
            else r=d-1;
        }
        if (l==min(tp,R-L+1))
            return tp>=R-L+1;
        return str[L+l]<now[l+1];
    }
    bool J(){
        int res=0;
        for (int l=n,r=n;l;){
            while(cmp(l,r) && l) l--;
            if (l==r) return 0;res++;r=l;
            if (res>k) return 0;
        }
        return 1;
    }
    int main(){
        scanf("%d%s",&k,str+1);
        b[0]=1;n=strlen(str+1);
        for (int i=1;i<=n;i++)
            build(str[i]-'a'),b[i]=b[i-1]*K,
            h[i][0]=h[i-1][0]*K+str[i];
        for (int i=1;i<=sz;i++) e[a[i].len]++,f[i]=1;
        for (int i=1;i<=sz;i++) e[i]+=e[i-1];f[1]=0;
        for (int i=1;i<=sz;i++) t[e[a[i].len]--]=i;
        for (int j,i=sz;i;i--){
            j=t[i];s[j]=f[j];
            for (int l=0;l<26;l++)
                s[j]+=s[a[j].nx[l]];
        }
        LL l=1,r=s[1],d;while(l<r){
            d=(l+r)>>1;tp=0;find(1,d);
            for (int i=1;i<=tp;i++)
                h[i][1]=h[i-1][1]*K+now[i];
            if (J()) r=d;else l=d+1;
        }
        tp=0;find(1,l);
        for (int i=1;i<=tp;i++) putchar(now[i]);putchar('
    ');
        return 0; 
    }
  • 相关阅读:
    Nginx 反向代理多个后台服务端口
    微信小程序,横向布局,纵向布局
    Maven的标准settings.xml文件
    Springboot 复杂查询及SQL拼接笔记
    ElementUI 设置显示侧栏滚动条elscrollbar,隐藏横向滚动条
    让俺内牛满面的编辑器啊~
    Javascript 对话框 (遇到 Ajax Load无法加载问题)
    thinkpad s5 电源功率不足提示
    NAO机器人开发环境配置
    Choregraphe 2.8.6.23动作失效
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/10667193.html
Copyright © 2011-2022 走看看