[TJOI2015]弦论 题解(第k小子串)

题意:
对于一个给定的长度为n的字符串,求出它的第k小子串。
有参数t,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。

题解:
首先,因为t的原因,后缀数组较难实现,这里不讨论。

使用后缀自动机:
因为,这里需要按字典序考虑子串,所以要使用trs指针。
首先,计算出每个子串的贡献:t=0则为1,t=1则为出现次数。
然后,通过记搜算出匹配到每个点之后可以形成多少贡献。因为使用trs,无需考虑压缩。
最后,在每个节点处找到唯一一个应当向下计算的点,循环直到找到解。

代码:

#include <stdio.h>
#define ll long long
int trs[1000010][26],fa[1000010],len[1000010],sl=1,la=1;
int su[1000010];ll dp[1000010];
void insert(int c)
{
    int np=++sl,p=la;
    len[np]=len[la]+1;la=np;
    while(p!=0&&trs[p][c]==0)
    {
        trs[p][c]=np;
        p=fa[p];
    }
    if(p==0)
        fa[np]=1;
    else
    {
        int q=trs[p][c];
        if(len[q]==len[p]+1)
            fa[np]=q;
        else
        {
            int nq=++sl;
            len[nq]=len[p]+1;
            fa[nq]=fa[q];fa[q]=fa[np]=nq;
            for(int i=0;i<26;i++)
                trs[nq][i]=trs[q][i];
            while(p!=0&&trs[p][c]==q)
            {
                trs[p][c]=nq;
                p=fa[p];
            }
        }
    }
    su[la]=1;
}
int fr[1000010],ne[1000010],v[1000010],bs=0;
void addb(int a,int b)
{
    v[bs]=b;
    ne[bs]=fr[a];
    fr[a]=bs++;
}
void build()
{
    for(int i=1;i<=sl;i++)
        fr[i]=-1;
    for(int i=2;i<=sl;i++)
        addb(fa[i],i);
}
void dfs0(int u)
{
    for(int i=fr[u];i!=-1;i=ne[i])
    {
        dfs0(v[i]);
        su[u]|=su[v[i]];
    }
}
void dfs1(int u)
{
    for(int i=fr[u];i!=-1;i=ne[i])
    {
        dfs1(v[i]);
        su[u]+=su[v[i]];
    }
}
void dfs2(int u)
{
    if(dp[u])
        return;
    dp[u]=su[u];
    for(int i=0;i<26;i++)
    {
        if(trs[u][i])
        {
            dfs2(trs[u][i]);
            dp[u]+=dp[trs[u][i]];
        }
    }
}
char zf[500010];
int main()
{
    int t,k,u=1;
    scanf("%s%d%d",zf,&t,&k);
    for(int i=0;zf[i]!=0;i++)
        insert(zf[i]-'a');
    build();
    if(t==0)dfs0(1);
    else dfs1(1);
    dfs2(1);
    if(dp[1]-su[1]<k)
    {
        printf("-1");
        return 0;
    }
    while(1)
    {
        if(u!=1)
        {
            if(su[u]>=k)
                break;
            k-=su[u];
        }
        int i;
        for(i=0;i<26;i++)
        {
            if(trs[u][i]==0)
                continue;
            if(dp[trs[u][i]]>=k)
                break;
            k-=dp[trs[u][i]];
        }
        printf("%c",i+'a');
        u=trs[u][i];
    }
    return 0;
}

原文地址:https://www.cnblogs.com/lnzwz/p/11616301.html

时间: 2024-11-13 08:16:31

[TJOI2015]弦论 题解(第k小子串)的相关文章

HDU 5008 求第k小子串

本题要求第k小的distinct子串,可以根据height数组,二分出这个第k小子串所在后缀的位置信息.由于题目要求子串起始下标尽可能小.所以再在rank数组中,二分出与当前后缀LCP大于等于所求子串长度的范围.通过RMQ求出这个范围中最小的sa. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string

HDU 5008西安网络赛B题:后缀数组求第k小子串

思路:尼玛,这题搞了一天了,比赛的时候用了n^2的方法绝对T了,然后今天看别人代码看了一天才知道.后面感觉也挺容易的,就是没想到,之前做过SPOJ 694 705求过不同子串了,知道怎么求不同子串个数了,但是比赛的时候这个技巧竟然抛在脑后了,然后就不会了. 但是今天自己用了自己的两个后缀数组的模板(倍增和DC3)的都WA了,搞得自己真想跳楼去了!! 到现在都不知道到底是哪里错了,处理的方法和标准做法都一样,但是就是WA,然后用了别人的模板,再用自己的处理方法就过了,怀疑自己的两个模板是不是哪里错

SPOJ SUBLEX 求第k小子串

题目大意: 对于一个给定字符串,找到其所有不同的子串中排第k小的子串 先构建后缀自动机,然后我们可以将整个后缀自动机看做是一个DAG图,那么我们先进行拓扑排序得到 *b[N] 对于每个节点记录一个sc值,表示当前节点往下走可以得到不同的字符串的个数 然后从后往前,每次到达一个节点,当前节点sc赋1,然后每个可以往下走的son节点,都把这个son上的sc加到当前节点上即可 接下来得到一个排名,从root开始走,从a~z循环,通过sc正确的找到下一个进入的节点 1 #include <cstdio>

bzoj 3998: [TJOI2015]弦论(后缀自动机)

题目链接:bzoj 3998: [TJOI2015]弦论 题意: 对于一个给定长度为N的字符串,求它的第K小子串是什么. 题解: 后缀自动机O(n)*26解决. 对于op=0,num[i]=1,对于op=1,num[i]=cnt[i]. 因为cnt[i](即right集)表示以i节点结尾的后缀出现的次数. 1 #include<cstdio> 2 #include<cstring> 3 #define F(i,a,b) for(int i=a;i<=b;++i) 4 #def

【BZOJ3998】[TJOI2015]弦论 后缀自动机

[BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. Output 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 Sample Input aabc 0 3 Sample Output aab HINT N<=5*10

P3975 [TJOI2015]弦论

题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在< String theory>中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么.你能帮帮她吗? 输入输出格式 输入格式: 第一行是一个仅由小写英文字母构成的字符串s 第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个.k的意义见题目描述. 输出格式: 输出数据仅有一行,该行有一个字符串,为第k小的子串.若子串数目不足k个,则输出-1. 输入输出样例 输入样

【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有相同前缀的串

BZOJ3998 [TJOI2015]弦论 【后缀自动机】

题目 对于一个给定长度为N的字符串,求它的第K小子串是什么. 输入格式 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. 输出格式 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 输入样例 aabc 0 3 输出样例 aab 提示 N<=5*10^5 T<2 K<=10^9 题解 肝了一个中午的论文还是想了好久这种裸题.. 由后缀自动机从根节点

Bzoj3998 [TJOI2015]弦论

Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2388  Solved: 798 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. Output 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 Sample In