zoukankan      html  css  js  c++  java
  • 【bzoj3998】[TJOI2015]弦论 后缀自动机+dp

    题目描述

    对于一个给定长度为N的字符串,求它的第K小子串是什么。

    输入

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

    第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。

    输出

    输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1

    样例输入

    aabc
    0 3

    样例输出

    aab


    题解

    后缀自动机+dp

    先对原串建立后缀自动机,然后在其上面跑dp统计每个节点开始的串的个数。

    设f[i]表示与位置i有相同前缀的串的个数。

    那么当T=0时,显然f[i]=∑f[son[i]]+1。

    当T=1时,f[i]=∑f[son[i]]+|right[i]|,需要统计right集合的大小,也即统计parent树中子树内有多少个叶子结点,这个递推一下即可。

    在这里边需要保证son[i]在i之前更新,所以需要得到拓扑序。

    然后大爷说会卡常?这里orz hzwer,对dis排序即可得到拓扑序,而且可以使用基数排序,详见代码。

    最后求一下和,dfs一遍就好了,类似于二(十六)分。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 1000010
    using namespace std;
    int n , opt , next[N][26] , fa[N] , dis[N] , last = 1 , tot = 1 , v[N] , q[N] , cnt[N] , sum[N];
    char str[N];
    void ins(int c)
    {
        int p = last , np = last = ++tot;
        dis[np] = dis[p] + 1 , cnt[np] = 1;
        while(p && !next[p][c]) next[p][c] = np , p = fa[p];
        if(!p) fa[np] = 1;
        else
        {
            int q = next[p][c];
            if(dis[q] == dis[p] + 1) fa[np] = q;
            else
            {
                int nq = ++tot;
                memcpy(next[nq] , next[q] , sizeof(next[q])) , dis[nq] = dis[p] + 1 , fa[nq] = fa[q] , fa[np] = fa[q] = nq;
                while(p && next[p][c] == q) next[p][c] = nq , p = fa[p];
            }
        }
    }
    void init()
    {
        int i , j , t;
        for(i = 1 ; i <= tot ; i ++ ) v[dis[i]] ++ ;
        for(i = 1 ; i <= n ; i ++ ) v[i] += v[i - 1];
        for(i = tot ; i ; i -- ) q[v[dis[i]] -- ] = i;
        for(i = tot ; i ; i -- )
        {
            t = q[i];
            if(opt) cnt[fa[t]] += cnt[t];
            else cnt[t] = 1;
        }
        cnt[1] = 0;
        for(i = tot ; i ; i -- )
        {
            t = q[i] , sum[t] = cnt[t];
            for(j = 0 ; j < 26 ; j ++ )
                sum[t] += sum[next[t][j]];
        }
    }
    void query(int p , int k)
    {
        if(k <= cnt[p]) return;
        k -= cnt[p];
        int i;
        for(i = 0 ; i < 26 ; i ++ )
        {
            if(next[p][i])
            {
                if(k <= sum[next[p][i]])
                {
                    putchar(i + 'a') , query(next[p][i] , k);
                    return;
                }
                k -= sum[next[p][i]];
            }
        }
    }
    int main()
    {
        int k , i;
        scanf("%s%d%d" , str + 1 , &opt , &k) , n = strlen(str + 1);
        for(i = 1 ; i <= n ; i ++ ) ins(str[i] - 'a');
        init();
        if(k > sum[1]) printf("-1");
        else query(1 , k);
        return 0;
    }
    
  • 相关阅读:
    Oracle 语法中的 INSERT INTO
    [Oracle]高效的SQL语句之分析函数(一)sum()
    Oracle:trunc()函数简介
    ORACLE 调试输出,字符串执行函数
    Oracle 的几种循环方式介绍
    js 判断字符串是否存在某个字符串
    IntelliJ IDEA 2021.3 旗舰版 官方中文正式版(附汉化包+安装教程)
    主线程中同步的 XMLHttpRequest 已不推荐使用,因其对终端用户的用户体验存在负面影响。可访问 http://xhr.spec.whatwg.org/ 详细了解
    js杂记:x:function(){}
    ORACLE 两表关联更新三种方式
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6951055.html
Copyright © 2011-2022 走看看