版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/88687227
题目:BZOJ3998.
题目大意:给定一个串
和一个数
,求
的第
小子串,若
表示子串重复算一个,否则不算一个,若子串数不到
个输出
.
.
首先看到子串就对 建立SAM.
建立SAM后,我们根据 分类讨论,先考虑 的情况.
首先着第 小的子串可以先考虑直接从原点出发,从小到大枚举所有字符转移边.若这条字符转移边到达的状态可以构成的子串数量不足 个,则肯定不往这条字符转移边转移,直接让 减掉这个值,然后尝试下一条字符转移边;否则进入这条字符转移边即可.
考虑何时停止,很明显通过之前的路径到达当前状态构成的子串是唯一的,所以只要当前 为 就可以停止了.
再来考虑如何求出每一个状态开始可以构成的子串数量,很明显我们可以用DP来预处理出这些值,设
为到状态
可以构成的数量,那么:
的情况其实类似,只是这样子一个状态可以表示的子串数量就要乘上它所代表的Right集合大小了.
时间复杂度 .
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500000,C=26;
int n,t,k;
char c[N+9],sc[N+9];
struct automaton{
int s[C],len,par;
}tr[N*2+9];
int cn,last,rght[N*2+9];
void Build_sam(){last=cn=1;}
void extend(int x){
int np=++cn,p=last;
tr[np].len=tr[p].len+1;rght[np]=1;
last=np;
while (p&&!tr[p].s[x]) tr[p].s[x]=np,p=tr[p].par;
if (!p) tr[np].par=1;
else{
int q=tr[p].s[x];
if (tr[p].len+1==tr[q].len) tr[np].par=q;
else{
tr[++cn]=tr[q];tr[cn].len=tr[p].len+1;
tr[q].par=tr[np].par=cn;
while (p&&tr[p].s[x]==q) tr[p].s[x]=cn,p=tr[p].par;
}
}
}
int q[N*2+9],v[N+9],sum[N*2+9];
void pre(){
int tt;
for (int i=1;i<=cn;++i) ++v[tr[i].len];
for (int i=1;i<=n;++i) v[i]+=v[i-1];
for (int i=cn;i>=1;--i) q[v[tr[i].len]--]=i;
for (int i=cn;i>=1;--i)
if (t==1) rght[tr[q[i]].par]+=rght[q[i]];
else rght[q[i]]=1;
rght[1]=0;
for (int i=cn;i>=1;--i){
tt=q[i];sum[tt]=rght[tt];
for (int j=0;j<C;++j)
sum[tt]+=sum[tr[tt].s[j]];
}
}
void dfs(int x,int pos){
if (k<=rght[x]) {k=0;return;}
k-=rght[x];
for (int i=0;i<C;++i)
if (tr[x].s[i]){
if (k<=sum[tr[x].s[i]]){
sc[pos]='a'+i;
dfs(tr[x].s[i],pos+1);
return;
}
k-=sum[tr[x].s[i]];
}
}
Abigail into(){
scanf("%s",c+1);
n=strlen(c+1);
scanf("%d%d",&t,&k);
}
Abigail work(){
Build_sam();
for (int i=1;i<=n;++i)
extend(c[i]-'a');
pre();
dfs(1,1);
}
Abigail outo(){
k>0?puts("-1"):printf("%s\n",sc+1);
}
int main(){
into();
work();
outo();
return 0;
}