[bzoj 3998][TJOI2015]弦论

传送门

Description

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

\(T=0\)则表示不同位置的相同子串算作一个。\(T=1\)则表示不同位置的相同子串算作多个。

Solution

\(SAM\)可以用来维护子串的信息,而相类似的子串会由同一个状态来维护。

当\(T=0\)时,我们发现不需要维护\(Right\)集合的大小,我们不妨直接当它是\(1\)

为了查询答案,我们需要记录一下每个状态后继的大小和。

Code?

//2019.1.26 23:20~00:07
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define MN 1000005
int c[MN][26],step[MN],val[MN],fa[MN],siz[MN],v[MN],rk[MN];
int last,cnt,n;
inline void init()
{
    last=cnt=1;memset(c,0,sizeof c);
    register int i;
    for(i=1;i<n<<1;++i) val[i]=step[i]=fa[i]=0;
}

void Insert(int x)
{
    int p=last,np=++cnt;step[np]=step[p]+1;val[np]=1;
    for(;p&&!c[p][x];p=fa[p]) c[p][x]=np;
    if(!p) fa[np]=1;
    else
    {
        int q=c[p][x];
        if(step[q]==step[p]+1) fa[np]=q;
        else
        {
            int nq=++cnt;step[nq]=step[p]+1;
            memcpy(c[nq],c[q],sizeof c[q]);
            fa[nq]=fa[q];fa[np]=fa[q]=nq;
            for(;c[p][x]==q;p=fa[p]) c[p][x]=nq;
        }
    }
    last=np;
}
inline void work()
{
    register int i;
    for(i=1;i<=cnt;++i) ++v[step[i]];
    for(i=1;i<=n;++i) v[i]+=v[i-1];
    for(i=1;i<=cnt;++i) rk[v[step[i]]--]=i;
    for(i=cnt;i;--i) val[fa[rk[i]]]+=val[rk[i]],siz[rk[i]]=val[rk[i]];
    val[1]=siz[1]=0;
}
char s[MN],ans[MN];int len;
inline void dfs(int x,int k)
{
    if(k<=val[x]) return;k-=val[x];
    register int i;
    for(i=0;i<26;++i)
        if(k>siz[c[x][i]]) k-=siz[c[x][i]];
        else {ans[len++]=i+'a',dfs(c[x][i],k);break;}
}
int main()
{
    register int i,j,k,type;
    scanf("%s%d%d",s+1,&type,&k);n=strlen(s+1);
    init();
    for(i=1;i<=n;++i) Insert(s[i]-'a');
    work();
    if(!type) for(i=2;i<=cnt;++i) val[i]=siz[i]=1;
    for(i=cnt;i;--i)for(j=0;j<26;++j)if(c[rk[i]][j])siz[rk[i]]+=siz[c[rk[i]][j]];
    if(siz[1]<k) puts("-1");
    else dfs(1,k),printf("%s",ans);
    return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10325390.html

时间: 2024-11-13 14:37:14

[bzoj 3998][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

●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时, 这时考虑了相同的子串.但是不难

【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

bzoj 3997: [TJOI2015]组合数学

3997: [TJOI2015]组合数学 Description 给出一个网格图,其中某些格子有财宝,每次从左上角出发,只能向下或右走.问至少走多少次才能将财宝捡完.此对此问题变形,假设每个格子中有好多财宝,而每一次经过一个格子至多只能捡走一块财宝,至少走多少次才能把财宝全部捡完. Input 第一行为正整数T,代表数据组数. 每组数据第一行为正整数N,M代表网格图有N行M列,接下来N行每行M个非负整数,表示此格子中财宝数量,0代表没有 Output 输出一个整数,表示至少要走多少次. Samp

BZOJ 4000: [TJOI2015]棋盘( 状压dp + 矩阵快速幂 )

状压dp, 然后转移都是一样的, 矩阵乘法+快速幂就行啦. O(logN*2^(3m)) --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define b(x) (1 <&l

P3975 [TJOI2015]弦论

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

[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]

bzoj 3998

后缀自动机上dfs,查询第k大子串 注意代码复杂度,可以不需要加边,写简洁的代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 #define maxn 1000020 7 8 struct node{ 9 int val,pnt,size,degree; 10 int next[27]; 1

BZOJ 3998 后缀数组

思路: 第一问 建出来后缀数组以后  前缀和一发n-sa[i]-ht[i]+1  二分 第二问 二分判断是带重复的第几 怎么判断呢   找到它  往后扫ht递减sum+=它   跟K判判 注意等于 加一 之类的各种坑爹细节 要死.. //By SiriusRen #include <bits/stdc++.h> using namespace std; const int N=1000050; int n,cntA[N],cntB[N],A[N],B[N],rk[N],sa[N],tsa[N]