「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

 

原文地址:https://www.cnblogs.com/shuaihui520/p/10695207.html