• 「BZOJ3998」[TJOI2015] 弦论(第K小子串)


    https://www.lydsy.com/JudgeOnline/problem.php?id=3998

    Description

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

    Input

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

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

    Output

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

    Sample Input

    aabc
    0 3

    Sample Output

    aab
     

    Sum值代表从当前状态出发不同的路径条数,即将孩子们的路径条数累加起来,再加上本身的s值。即sum[i]=s[i]+∑sum[j](j=next[i][k],k=0..25)

    预处理结束之后,通过dfs找出第k小的路径。这有点类似与二十六分,每次先按字典序往后走,如果当前节点的s值大于当前的k,则说明到当前节点为止,退出dfs;否则k先减去当前s的大小。如果当前节点的sum值大于当前的k值,说明终止点再它的孩子中,输出当前节点对应的字母,k并继续往下深dfs;如果当前结点的sum值小于k,说明k大的子串不在这条路径上,直接将k减去sum并继续搜索下一条路径。(说起来有点绕,直接看代码)

    #include <bits/stdc++.h>
    #define LL long long
    #define P pair<int, int>
    #define lowbit(x) (x & -x)
    #define mem(a, b) memset(a, b, sizeof(a))
    #define rep(i, a, n) for (int i = a; i <= n; ++i)
    const int maxn = 500001;
    #define mid ((l + r) >> 1)
    #define lc rt<<1
    #define rc rt<<1|1
    using namespace std;
    // __int128 read() {    __int128 x = 0, f = 1;  char c = getchar(); while (c < '0' || c > '9') {        if (c == '-') f = -1;       c = getchar();  }   while (c >= '0' && c <= '9') {      x = x * 10 + c - '0';       c = getchar();  }   return x * f;}
    // void print(__int128 x) { if (x < 0) {        putchar('-');       x = -x; }   if (x > 9)  print(x / 10);  putchar(x % 10 + '0');}
    const LL mod = 1e9 + 7;
    int len,T,k;
    struct SAM{
    
        int trans[maxn<<1][26], slink[maxn<<1], maxlen[maxn<<1];
        // 用来求endpos
        int indegree[maxn<<1], endpos[maxn<<1], rank[maxn<<1], ans[maxn<<1];
        // 计算所有子串的和(0-9表示)
        LL sum[maxn<<1];
        int last, now, root;
    
        inline void newnode (int v) {
            maxlen[++now] = v;
            mem(trans[now],0);
        }
    
        inline void extend(int c) {
            newnode(maxlen[last] + 1);
            int p = last, np = now;
            // 更新trans
            while (p && !trans[p][c]) {
                trans[p][c] = np;
                p = slink[p];
            }
            if (!p) slink[np] = root;
            else {
                int q = trans[p][c];
                if (maxlen[p] + 1 != maxlen[q]) {
                    // 将q点拆出nq,使得maxlen[p] + 1 == maxlen[q]
                    newnode(maxlen[p] + 1);
                    int nq = now;
                    memcpy(trans[nq], trans[q], sizeof(trans[q]));
                    slink[nq] = slink[q];
                    slink[q] = slink[np] = nq;
                    while (p && trans[p][c] == q) {
                        trans[p][c] = nq;
                        p = slink[p];
                    }
                }else slink[np] = q;
            }
            last = np;
            // 初始状态为可接受状态
            endpos[np] = 1;
        }
    
    
        inline void init()
        {
            root = last = now = 1;
            slink[root]=0;
            mem(trans[root],0);
        }
    
        inline void getEndpos() {
            // topsort
            for (int i = 1; i <= now; ++i) indegree[ maxlen[i] ]++; // 统计相同度数的节点的个数
            for (int i = 1; i <= now; ++i) indegree[i] += indegree[i-1];  // 统计度数小于等于 i 的节点的总数
            for (int i = 1; i <= now; ++i) rank[ indegree[ maxlen[i] ]-- ] = i;  // 为每个节点编号,节点度数越大编号越靠后
            // 从下往上按照slik更新
            for (int i = now; i >= 1; --i) {
                int x = rank[i];
              //  printf("%d ",x);
                if(T==1)
                endpos[slink[x]] += endpos[x];
                else endpos[x]=1;
            }
            endpos[1]=0;//不要忘了根节点是虚点
            for(int i=now ; i>=1 ; i--)
            {
                int x = rank[i];
                sum[x]=endpos[x];
                for(int j=0 ; j<26 ; j++)///后面可以接的字符
                sum[x]+=sum[trans[x][j]];
            }
        }
        void dfs(int x,int K)
        {
            if(K<=endpos[x]) return ;
            K-=endpos[x];
    
            for(int i=0 ; i<26 ; i++)
            {
                int p=trans[x][i];
                if(p)
                {
                    if(K<=sum[p])
                    {
                        printf("%c",i+'a');
                        dfs(p,K);
                        return ;
                    }
                    K-=sum[p];
                }
            }
        }
    
    
    }sam;
    
    int main()
    {
    
        string str;cin>>str>>T>>k;
        sam.init();
        len=str.size();
        for(int i=0 ; i<len ; i++)
        sam.extend(str[i]-'a');
        sam.getEndpos();
        sam.dfs(sam.root , k);
       //- sam.all();
    }
    View Code

     

  • 相关阅读:
    转载__Java内部类
    Fragment之介绍(转)
    转载__Android-屏幕适配需要注意的地方
    转载__广播机制
    Activity的启动模式--总结
    图片_ _Android--加载大分辨率图片到内存
    转载—— android 瀑布流的实现详解,附源码
    转载_安卓性能优化
    C# Byte[] 数组操作
    C# 测算代码运行时间 Stopwatch
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/10695207.html
Copyright © 2020-2023  润新知