题目链接
https://www.luogu.com.cn/problem/P3975
题意
给你一个仅由小写字母构成的字符串(s),输出它的第(k)小子串((t=0)时表示不同位置的相同子串算作一个,(t=1)时表示不同位置的相同子串算作多个)
思路
构建出SAM之后,求出(sum[i]),表示有(sum[i])个子串经过(i)号点。
(siz[i])表示(i)所代表的(endpos)的集合大小,也就是(i)所对应字符串集合的出现次数。
(t=0)时,本质相同的子串在不同位置出现算相同,所以(siz[i]=1),即将每个字符串集合的(endpos)集合大小(字符串集合元素出现次数)置为(1)
(t=1)时,本质相同的子串在不同位置出现算不同,那么累加后的(siz)表示实际上(endpos)的集合大小
在SAM的树结构上(dp)求(siz)
在SAM的图结构上(dp)求(sum)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxx = 2*1e6+10;
char s[maxx];
int last=1,tot=1,fa[maxx],ch[maxx][26],len[maxx];
LL siz[maxx],sum[maxx];
void add(int x)
{
int pre=last,now=last=++tot;
len[now]=len[pre]+1;siz[now]=1;
for(;pre&&!ch[pre][x];pre=fa[pre])ch[pre][x]=now;
if(!pre)fa[now]=1;
else
{
int q=ch[pre][x];
if(len[q]==len[pre]+1)fa[now]=q;
else
{
int nows=++tot;
len[nows]=len[pre]+1;
memcpy(ch[nows],ch[q],sizeof(ch[q]));
fa[nows]=fa[q];fa[q]=fa[now]=nows;
for(;pre&&ch[pre][x]==q;pre=fa[pre])ch[pre][x]=nows;
}
}
}
int head[maxx],to[maxx],ne[maxx],cnt;
int vis[maxx];
void addm(int u,int v)
{
to[++cnt]=v,ne[cnt]=head[u],head[u]=cnt;
}
void dfs1(int u)
{
for(int i=head[u];i;i=ne[i])
{
dfs1(to[i]);
siz[u]+=siz[to[i]];
}
}
void dfs2(int u)
{
if(vis[u])return;
vis[u]=1;
for(int i=0;i<26;i++)
{
int v=ch[u][i];
if(v)dfs2(v),sum[u]+=sum[v];
}
}
void solve(int u,int k)
{
if(k<=siz[u])return;
k-=siz[u];
for(int i=0;i<26;i++)
{
int v=ch[u][i];
if(!v)continue;
if(k>sum[v])k-=sum[v];
else
{
putchar(i+'a');
solve(v,k);
return;
}
}
}
int main()
{
scanf("%s",s+1);
int t,k;
scanf("%d%d",&t,&k);
int n=strlen(s+1);
for(int i=1;i<=n;i++)add(s[i]-'a');
for(int i=2;i<=tot;i++)addm(fa[i],i);
dfs1(1);
for(int i=1;i<=tot;i++)
{
if(t)sum[i]=siz[i];
else sum[i]=siz[i]=1;
}
sum[1]=siz[1]=0; //根的点权为0
dfs2(1);
if(sum[1]<k)puts("-1");
else solve(1,k),puts("");
return 0;
}