版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/85200644
BZOJ传送门
洛谷传送门
解析:
在本校OJ上又递归爆栈空间了。。。不过手动汇编开栈后是OJ上跑的最快的。。。
思路:
对于只考虑本质不同的字串的情况,就是SAM上跳一个fail链统计一下本质不同字串个数,然后一个节点的本质不同字串总和就是它DAG上所有后继状态的本质不同字串数之和,当然还要考虑它自己的cnt。
不过为了过会的字串查找,需要单独统计当前状态所有后继之和。即我们需要把cnt留着。
不然的话,每个状态都是一个全新的字串,cnt赋为1,DAG上DP一下就行了
强行令 ,不考虑空状态。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
cs int N=500005;
struct SAM{
int son[N<<1][26],fa[N<<1],len[N<<1],siz[N<<1],cnt[N<<1];
int now,last,l;
SAM():now(1),last(1){}
inline void push_back(char c){
c-='a';++l;
int cur=++now;
int p=last;
len[cur]=len[last]+1;cnt[cur]=1;
for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
if(!p)fa[cur]=1;
else if(len[son[p][c]]==len[p]+1)fa[cur]=son[p][c];
else {
int clone=++now,q=son[p][c];
memcpy(son[clone],son[q],sizeof son[q]);
len[clone]=len[p]+1;
fa[clone]=fa[q];
fa[q]=fa[cur]=clone;
for(;p&&son[p][c]==q;p=fa[p])son[p][c]=clone;
}
last=cur;
}
inline void calc(int op){
static int bin[N],a[N<<1];
for(int re i=1;i<=now;++i)++bin[len[i]];
for(int re i=1;i<=l;++i)bin[i]+=bin[i-1];
for(int re i=1;i<=now;++i)a[bin[len[i]]--]=i;
for(int re i=now;i;--i)op?(cnt[fa[a[i]]]+=cnt[a[i]]):(cnt[i]=1);
cnt[1]=0;
for(int re i=now;i;--i){
siz[a[i]]=cnt[a[i]];
for(int re j=0;j<26;++j)
if(son[a[i]][j])siz[a[i]]+=siz[son[a[i]][j]];
}
}
inline void dfs(int now,int k){
if((k-=cnt[now])<=0)return ;
for(int re i=0;i<26;++i){
if(son[now][i]){
if(k<=siz[son[now][i]])return pc('a'+i),dfs(son[now][i],k);
k-=siz[son[now][i]];
}
}
}
inline void query(int k){
if(k>siz[1])puts("-1");
else dfs(1,k);
}
}sam;
char s[N];int len;
signed main(){
scanf("%s",s+1);len=strlen(s+1);
for(int re i=1;i<=len;++i)sam.push_back(s[i]);
int op,k;
scanf("%d%d",&op,&k);
sam.calc(op);
sam.query(k);
return 0;
}