P3975 [TJOI2015]弦论

题目描述

为了提高智商,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=1,不同位置相同子串看做不同;t=0:,不同位置的相同子串看成相同;

先对这个串建个SAM,再对所有节点按照maxlen排下序。这样就可以从长到短地总结答案。

首先对每个节点维护一个当前节点所占的子串个数。假如t=1就按照Parent树里从叶子节点一层一层往上推的顺序,计算每个串出现的次数。t=0就不用管,直接设成1

这样就可以维护出每个节点往后推会有多少个串。。(直接加起来

然后在SAM上按照字典序往下匹配就可以了

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=1e6+10;
char s[maxn];
int k,t;
struct SAM{//求字典序第K小串
    int l[maxn<<1],fa[maxn<<1],nxt[maxn<<1][26];
    int last,cnt,c[maxn<<1],siz[maxn<<1],sum[maxn<<1],a[maxn<<1];
    void Init()
    {
        memset(siz,0,sizeof(siz));
        memset(c,0,sizeof(c));
        memset(sum,0,sizeof(sum));
        memset(a,0,sizeof(a));
        last=cnt=1;
        memset(nxt[1],0,sizeof(nxt[1]));
        fa[1]=l[1]=0;
    }
    int NewNode()
    {
        cnt++;
        memset(nxt[cnt],0,sizeof(nxt[cnt]));
        fa[cnt]=l[cnt]=0;
        return cnt;
    }
    void Add(int ch)
    {
        int p=last,np=NewNode();
        last=np; l[np]=l[p]+1;
        siz[np]=1;
        while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p];
        if(!p) fa[np]=1;
        else
        {
            int q=nxt[p][ch];
            if(l[q]==l[p]+1) fa[np]=q;
            else
            {
                int nq=NewNode();
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq]=fa[q];
                l[nq]=l[p]+1;
                fa[np]=fa[q]=nq;
                while(nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p];
            }
        }
    }
    void Build()
    {
        int len=strlen(s+1);
        for(int i=1;i<=len;i++) Add(s[i]-‘a‘);
    }
    void topusort()
    {
        for(int i=1;i<=cnt;++i) c[l[i]]++;
        for(int i=1;i<=cnt;++i) c[i]+=c[i-1];
        for(int i=1;i<=cnt;++i) a[c[l[i]]--]=i;
        for(int i=cnt;i;--i)
        {//t==1:不同位置的相同子串视为不同
            if(t) siz[fa[a[i]]]+=siz[a[i]];
            else siz[a[i]]=1;//t==0:视为相同
        }
        siz[1]=0;
        for(int i=cnt;i;--i)
        {
            sum[a[i]]=siz[a[i]];
            for(int j=0;j<26;++j)
                if(nxt[a[i]][j]) sum[a[i]]+=sum[nxt[a[i]][j]];
        }
    }
    void dfs()
    {
        if(k>sum[1]){puts("-1");return ;}
        int now=1;
        while(k>0)
        {
            int p=0;
            while(k>sum[nxt[now][p]])
            {
                k-=sum[nxt[now][p]];
                p++;
            }
            now=nxt[now][p];
            putchar(‘a‘+p);
            k-=siz[now];
        }
        return ;
    }
} sam;
int main()
{
    scanf("%s%d%d",s+1,&t,&k);
    sam.Init();
    sam.Build();
    sam.topusort();
    sam.dfs();
    return 0;
}

原文地址:https://www.cnblogs.com/songorz/p/10805198.html

时间: 2024-10-09 03:01:53

P3975 [TJOI2015]弦论的相关文章

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

[TJOI2015]弦论 - 后缀自动机

下了狠心开始做SAM的题目了-- (中间因为傻逼26分写错被卡,进来的时候记得把自己的 cnt 减掉) // TJOI2015 XIAN LUN #include <bits/stdc++.h> using namespace std; const int Maxn = 2000005; struct Suffix_Automata { int maxlen[Maxn], trans[Maxn][26], link[Maxn], Size, Last; int t[Maxn], a[Maxn]

BZOJ3998 TJOI2015 弦论 后缀自动机

题意:求一个字符串的第K小字串,T=0表示不同位置相同的子串算作一个,T=1算作多个 题意: 建出SAM来跑第K子串,由于一个点所代表的子串在原串出现次数为其子树叶子结点的数量,因而有: T==1,每个点的|right|=1 T==2,每个点的|right|=子树叶子结点数 BFS跑出所有子串出现的次数即|right|,DFS统计每个节点的出现次数和,具体细节看代码. 然后处理询问即可. #include <cstdio> #include <cstring> #include &

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

bzoj3998 [TJOI2015]弦论(SAM)

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

【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 题解 肝了一个中午的论文还是想了好久这种裸题.. 由后缀自动机从根节点

●BZOJ 3998 [TJOI2015]弦论

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3998题解: 后缀自动机. 当T=0时, 由于在后缀自动机上沿着trans转移,每个串都是互不相同的, 就只需要统计出从每个状态出发,存在多少条不同的路径,即有多少个不同的子串. 这个可以拓扑排序后用DP解决. 转移: $$cnt[p]=\sum_{trans[p][*]=q,q!=0} cnt[q] + 1$$ 然后配合cnt[]去dfs即可. 当T=1时, 这时考虑了相同的子串.但是不难