• 洛谷 P3975 / loj 2102 [TJOI2015] 弦论 题解【后缀自动机】【拓扑排序】


    后缀自动机入门。

    题目描述

    为了提高智商,ZJY 开始学习弦论。

    这一天,她在《String theory》中看到了这样一道问题:对于一个给定的长度为 (n) 的字符串,求出它的第 (k) 小子串是什么。你能帮帮她吗?

    输入输出格式

    输入格式:

    第一行是一个仅由小写英文字母构成的字符串 (s)

    第二行为两个整数 (t)(k)(t)(0) 则表示不同位置的相同子串算作一个。(t)(1) 则表示不同位置的相同子串算作多个。(k) 的意义见题目描述。

    输出格式:

    输出数据仅有一行,该行有一个字符串,为第 (k) 小的子串。如果子串数目不足 (k) 个,则输出 (-1)

    输入输出样例

    输入样例:

    aabc
    0 3
    

    输出样例:

    aab
    

    数据范围与约定

    对于 (10\%) 的数据,(nle 1000)

    对于 (50\%) 的数据,(t=0)

    对于 (100\%) 的数据,(nle 5 imes 10^5, t<2, kle 10^9)

    题解:

    SAM 学习笔记...先鸽一会。

    这个题是找出第 (k) 大子串。因为在字符串中,第一关键字是字符,而不是长度,所以可以一个字符一个字符的比对。

    那么我们先构建出 SAM,如果 (t=1),还需要跳 parent 树来计算一个串出现了多少次。记一个状态的贡献为 (r)

    然后还需要对 SAM 反向拓扑排序/记忆化搜索,得出走这条边可能到达多少种子串,也就是 DAG 后面每一条路径的 (r) 之和,记为 (f)

    然后从起始节点开始检测。每次遍历 a~z,如果发现那条边的 (f) 小于 (k),那么第 (k) 小的不在那边,此时让 (k) 减去 (f)。如果发现 (f) 大于 (k),则进入这条边。因为我们选了这条边,而后缀自动机的每一个状态都代表了一个子串,因此要把 (f) 减去新到点的 (r)

    如果此时发现 (kge 0),说明第 (k) 小的子串就在刚才那一步的状态里。否则继续在 SAM 上跑。

    时间复杂度 (O(n))

    Code:

    #include<cstdio>
    #include<cstring>
    struct edge
    {
        int n,nxt;
        edge(int n,int nxt)
        {
            this->n=n;
            this->nxt=nxt;
        }
        edge(){}
    }e[2000100];
    int head[1000100],ecnt=-1;
    void add(int from,int to)
    {
        e[++ecnt]=edge(to,head[from]);
        head[from]=ecnt;
    }
    char s[500100];
    int ch[26][1000100],mx[1000100],par[1000100],r[1000100],pcnt;
    void build()
    {
        int p=pcnt=1;
        for(int i=1;s[i]!='';++i)
        {
            int w=s[i]-'a';
            int np=++pcnt;
            mx[np]=mx[p]+1;
            r[np]=1;
            while(p&&!ch[w][p])
            {
                ch[w][p]=np;
                p=par[p];
            }
            if(!p)
                par[np]=1;
            else
            {
                int q=ch[w][p];
                if(mx[q]==mx[p]+1)
                    par[np]=q;
                else
                {
                    int nq=++pcnt;
                    mx[nq]=mx[p]+1;
                    while(p&&ch[w][p]==q)
                    {
                        ch[w][p]=nq;
                        p=par[p];
                    }
                    for(int j=0;j<26;++j)
                        ch[j][nq]=ch[j][q];
                    par[nq]=par[q];
                    par[q]=nq;
                    par[np]=nq;
                }
            }
            p=np;
        }
    }
    void dfs(int x)
    {
        for(int i=head[x];~i;i=e[i].nxt)
        {
            dfs(e[i].n);
            r[x]+=r[e[i].n];
        }
    }
    int d[1000100];
    int q[1000100],l=0,R=0;
    long long f[1000100];
    int main()
    {
        memset(head,-1,sizeof(head));
        scanf("%s",s+1);
        int t,k;
        scanf("%d%d",&t,&k);
        build();
        for(int i=2;i<=pcnt;++i)
            add(par[i],i);
        if(t)
            dfs(1);
        else
            for(int i=1;i<=pcnt;++i)
                r[i]=1;
        memset(head,-1,sizeof(head));
        ecnt=-1;
        for(int i=1;i<=pcnt;++i)
        {
            int flag=0;
            for(int j=0;j<26;++j)
                if(ch[j][i])
                {
                    flag=1;
                    ++d[i];
                    add(ch[j][i],i);
                }
            if(!flag)
                q[++R]=i;
        }
        while(l<R)
        {
            int x=q[++l];
            f[x]+=r[x];
            for(int i=head[x];~i;i=e[i].nxt)
            {
                f[e[i].n]+=f[x];
    
                --d[e[i].n];
                if(!d[e[i].n])
                    q[++R]=e[i].n;
            }
        }
        t=1;
        while(k>0)
        {
            int flag=0;
            for(int i=0;i<26;++i)
            {
                if(f[ch[i][t]]<k)
                    k-=f[ch[i][t]];
                else
                {
                    flag=1;
                    k-=r[ch[i][t]];
                    printf("%c",'a'+i);
                    t=ch[i][t];
                    break;
                }
            }
            if(!flag)
                break;
        }
        if(k>0)
            puts("-1");
        return 0;
    }
    
  • 相关阅读:
    eclipse无法启动问题记录
    java环境配置
    Tomcat配置,Myeclipse破解和各种设置
    Android_Service详解及实例
    WTF
    jQuery实现图片轮播
    jQuery实现皮肤自定义
    CSS中的transform与transition
    checkbox实现全选/全不选
    ng-if可见
  • 原文地址:https://www.cnblogs.com/wjyyy/p/lg3975.html
Copyright © 2020-2023  润新知