这个看似模板,但是我还是没有理解透彻,毕竟写得慢还是主要比不过抄码的人。
但是在ZJY巨子本色出演的题目,我还是请教了ZJY本人,我大抵是会了。
这个本身有两种情况
1)t=0求right集合(又叫endpos集合)
2)t=1求siz集合
这个都好求
然后就是一个类似于可持久化线段树的查询方式了
从小到大暴力减
减到0或负数就退出
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e6+5e5+100;
char S[N];
struct SAM{
struct Node{
int vis[26];
int step;
int pre;
int siz;
}SA[N];
int last,cnt;
int siz[N];
int num[N];
int C[N];
int A[N];
inline void Insert(char C){
int p=last;
int np=++cnt;
last=np;
SA[np].step=SA[p].step+1;
for(;!SA[p].vis[C-'a'];p=SA[p].pre)SA[p].vis[C-'a']=np;
if(!p) SA[np].pre=1;
else{
int q=SA[p].vis[C-'a'];
if(SA[q].step==SA[p].step+1){
SA[np].pre=q;
}
else{
int nq=++cnt;
SA[nq].step=SA[p].step+1;
memcpy(SA[nq].vis,SA[q].vis,sizeof(SA[q].vis));
SA[nq].pre=SA[q].pre;
SA[q].pre=SA[np].pre=nq;
for(;SA[p].vis[C-'a']==q;p=SA[p].pre)SA[p].vis[C-'a']=nq;
}
}
siz[np]=1;
}
void Solve(){
last=cnt=1;
int t,k;
scanf("%s%d%d",S+1,&t,&k);
int len=strlen(S+1);
for(int i=1;i<=len;i++)Insert(S[i]);
for(int i=1;i<=cnt;i++)C[SA[i].step]++;
for(int i=1;i<=len;i++)C[i]+=C[i-1];
for(int i=1;i<=cnt;i++)A[C[SA[i].step]--]=i;
// cout<<cnt<<'\n';
for(int i=cnt;i>=1;i--){
// cout<<A[i]<<" "<<SA[A[i]].pre<<'\n';
int p=A[i];
if(!t)siz[p]=1;
else{
int now=SA[p].pre;
siz[now]+=siz[p];
}
}
for(int i=cnt;i>=1;i--){
int p=A[i];
num[p]=siz[p];
// cout<<p<<'\n';
for(int j=0;j<26;j++){
if(!SA[p].vis[j])continue;
num[p]+=num[SA[p].vis[j]];
}
// cout<<num[p]<<'\n';
}
if(k>num[1]){
// cout<<num[1];
puts("-1");
return;
}
int p=0;
int now=1;
siz[1]=0;
num[0]=0;
// for(int i=1;i<=cnt;i++)cout<<num[i]<<'\n';
// cout<<siz[1]<<'\n';
while((k-=siz[now])>0){
int p=0;
while(k>num[SA[now].vis[p]])k-=num[SA[now].vis[p++]];
now=SA[now].vis[p];
// cout<<k<<" "<<now<<" "<<p<<'\n';
cout<<(char)('a'+p);
}
}
}Solution;
int main(){
freopen("2875.in","r",stdin);
// freopen("2875.out","w",stdout);
Solution.Solve();
return 0;
}