洛谷 P3975 [TJOI2015]弦论 后缀自动机

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82083066

题目描述

为了提高智商,ZJY开始学习弦论。这一天,她在《String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?

输入输出格式

输入格式:
第一行是一个仅由小写英文字母构成的字符串s

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

输出格式:
输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。

输入输出样例

输入样例#1:
aabc
0 3
输出样例#1:
aab
输入样例#2:
aabc
1 3
输出样例#2:
aa
输入样例#3:
aabc
1 11
输出样例#3:
-1
说明

数据范围

对于 10 % 的数据, n 1000
对于 50 % 的数据, t = 0
对于 100 % 的数据, n 5 × 10 5 , t < 2 , k 10 9

分析:
我们可以对这个串建后缀自动机。如果 t = 0 ,每个点的 s i z e 1 ,否则为 r i g h t 集大小。因为每一条路径都可以代表一个字符串,我们在 D A G 上跑出每个点为前缀的字符串有多少个。然后像权值线段树求第 k 大一样选择走哪条边,不过要先减掉当前点的 s i z e 1 号节点的 s i z e 不用减,因为不存在这样的串。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>

const int maxn=1e6+7;

using namespace std;

char s[maxn],ans[maxn];
int n,T,k,cnt,num;
int size[maxn],top[maxn],b[maxn],sum[maxn];

queue <int> q;

struct node{
    int len,fail;
    int son[26];
}t[maxn];

void build_sam()
{
    cnt=1;
    int now=1,p,q,clone;
    for (int i=0;i<n;i++)
    {
        int c=s[i]-'a';
        p=now;
        now=++cnt;
        t[now].len=t[p].len+1;
        size[now]=1;
        while (p&&(!t[p].son[c])) t[p].son[c]=now,p=t[p].fail;
        if (!p) t[now].fail=1;
        else
        {
            q=t[p].son[c];
            if (t[p].len+1==t[q].len) t[now].fail=q;
            else
            {
                clone=++cnt;
                t[clone]=t[q];
                t[clone].len=t[p].len+1;
                t[now].fail=t[q].fail=clone;
                while (p&&(t[p].son[c]==q)) t[p].son[c]=clone,p=t[p].fail;
            }
        }
    }
}

void prework()
{
    for (int i=1;i<=cnt;i++) b[t[i].len]++;
    for (int i=1;i<=n;i++) b[i]+=b[i-1];
    for (int i=1;i<=cnt;i++) top[b[t[i].len]--]=i;
    for (int i=cnt;i>0;i--) size[t[top[i]].fail]+=size[top[i]];
    for (int i=cnt;i>0;i--)
    {
        int x=top[i];
        if (!T) size[x]=1;
        sum[x]=size[x];
        for (int j=0;j<26;j++) sum[x]+=sum[t[x].son[j]];
    }
}

void solve()
{
    q.push(1);
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        if (x!=1)
        {
            if (k<=size[x]) return;
            k-=size[x];
        }
        for (int i=0;i<26;i++)
        {
            if (sum[t[x].son[i]]>=k)
            {
                ans[++num]='a'+i;
                q.push(t[x].son[i]);
                break;
            }
            else k-=sum[t[x].son[i]];
        }
    }

}

int main()
{
    scanf("%s",s);
    scanf("%d%d",&T,&k);
    n=strlen(s);
    build_sam();    
    prework();      
    solve();
    if (!num) printf("-1");
    else
    {
        for (int i=1;i<=num;i++) printf("%c",ans[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82083066
今日推荐