CSES - Shared codeLink to this code:
https://cses.fi/paste/1a1adab9babc8ef84a7c56/
#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
typedef pair<int, int> pii;
#define endl "\n"
#define all(v) v.begin(),v.end()
#define trace1(x) if(dm) cout<<(#x)<<" "<<(x)<<endl
#define trace2(x,y) if(dm) cout<<(#x)<<" "<<(x)<<", "<<(#y)<<" "<<(y)<<endl
#define trace3(x,y,z) if(dm) cout<<(#x)<<" "<<(x)<<", "<<(#y)<<" "<<(y)<<", "<<(#z)<<" "<<(z)<<endl
int dm = 0;
void fastio(){ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);}
void fileio() {
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
#endif
}
vector<int> suffix_array(const string& str){
// return sorted position of each suffix, 'pos'
// pos[k] = i => kth smallest suffix is suffix[i...n-1]
string s = str + '$'; // '$' is smaller than all character;
int n = s.size();
vector<int> clss(n, 0);
vector<int> pos(n,0);
// clss[i] = k => suffix s[i...n-1] is kth smallest suffix in sorted order.
// its like sort of inverse of pos[] array, and both arrays can be computed from each other
// ---------- Step 1
// build initial clss for only first character
vector<int> cnt(126,0);
vector<int> cntsum(127,0);
vector<vector<int>> idx_with_clss(n);
vector<int> newclss(n,-1);
for(int i=0; i<n; i++){
cnt[s[i]]++;
}
for(int i=1; i<=126; i++){
cntsum[i] = cntsum[i-1] + cnt[i-1];
}
for(int i=0; i<n; i++){
clss[i] = cntsum[ s[i] ] ;
pos[ cntsum[s[i]] + (cnt[s[i]]-1) ] = i;
cnt[s[i]]--;
}
// ----------- Step 2
int prevlen = 1;
int len = 2;
while(len<=n){
// we know the order for string of some prevlen >= len/2
// we want to order the shifts for length len [0...len-1]
// first order the shifts according to rank of their [prevlen...len-1]
// then we order then acc to rank of [0..prevlen-1]
// pos[k+x] = i, pos[k] = j
// implies i appears after j
// <=> for shift i, [prevlen...len-1] is having clss bigger than for shift j,[prevlen...len-1]
// order shift 'i' according to clss[(i+prevlen)%n]
// then order shift 'i' according to clss[i]
// Step A : order acc to clss[ (i+prevlen) % n]
for(int i=0; i<n; i++){
idx_with_clss[i].clear();
}
for(int i=0; i<n;i++){
idx_with_clss[ clss[(i+prevlen)%n] ].push_back(i);
}
int k=0;
for(int i=0; i<n; i++){
for(int idx : idx_with_clss[i]){
pos[k++] = idx;
}
}
// Step B : order acc to clss[i]
for(int i=0; i<n; i++){
idx_with_clss[i].clear();
}
for(int order=0; order<n;order++){
idx_with_clss[ clss[pos[order]] ].push_back(pos[order]);
}
k=0;
for(int i=0; i<n; i++){
for(int idx : idx_with_clss[i]){
pos[k++] = idx;
// can put the next loop here
}
}
assert(k==n);
// Step C : Find clss array using pos
newclss[pos[0]] = 0;
int curclss = 0;
for(int ord=1; ord<n; ord++){
// shift with pos[ord-1] <= shift with pos[ord] till length 'len'
// if greater then higher class otherwise same
int i=pos[ord-1];
int j=pos[ord];
if(clss[j]>clss[i] ||
(clss[i]==clss[j] && (clss[(j+prevlen)%n]>clss[(i+prevlen)%n]) ) )
{
curclss++;
}
newclss[j] = curclss;
}
for(int i=0; i<n; i++) clss[i] = newclss[i];
prevlen = len;
if(len==n) break;
len = len*2;
if(len>n) len = n;
}
// remove first element of pos
pos.erase(pos.begin());
return pos;
}
vector<int> lcp_array(const string&s, const vector<int>& sa){
int n = s.size();
vector<int> lcp(n-1,-1);
vector<int> rank(n);
for(int i=0; i<n; i++)rank[ sa[i] ] = i;
// there are n suffixes
// lcp[i] -> lcp of suffix[ sa[i] ] and suffix[ sa[i+1] ]
for(int idx=0; idx<n; idx++){
int i = rank[idx];
if(i == n-1){
continue;
}
if(lcp[i]==-1){
int idx1 = sa[i];
int idx2 = sa[i+1];
int curlcp = 0;
while(idx1<n && idx2<n && s[idx1]==s[idx2]){
idx1++,idx2++,curlcp++;
}
// maybe 0,1,2...
lcp[i] = curlcp;
curlcp--;
idx1 = sa[i]+1;
idx2 = sa[i+1]+1;
while(idx1<n && idx2<n && curlcp >= 0){
int rank1 = rank[idx1];
int rank2 = rank[idx2];
if(rank2 != rank1+1){
}
else{
lcp[rank1] = curlcp;
}
curlcp--, idx1++, idx2++;
}
}
}
return lcp;
}
class PrefixSumSALengths{
public:
vector<lli> psum;
int n;
PrefixSumSALengths(){
}
PrefixSumSALengths(const vector<int>& sa){
this->n = sa.size();
psum = vector<lli>(n+1, 0);
for(int i=1; i<=n; i++){
psum[i] = psum[i-1] + (n - sa[i-1]);
}
}
lli get_sum(int st, int end) const{
assert(end+1 <= n);
assert(st>=0);
return psum[end+1] - psum[st];
}
};
namespace spt{
const int MX = 27;
const int SZ = 200000;
int table[MX][SZ+1];
struct rmq{
int n;
rmq(){}
rmq(const vector<int>& arr){init(arr);}
void init(const vector<int>& arr)
{
n = arr.size();
assert(n <= SZ);
for(int idx=0; (1<<idx)<=n; idx++){
for(int i=0; i<n; i++){
if(idx==0){
table[idx][i] = arr[i];
}
else{
int sz = 1<<idx;
if(i+sz-1>=n) break;
table[idx][i] = min( table[idx-1][i], table[idx-1][i+sz/2]);
}
}
}
}
int getmin(int st, int end){
// sz = (end-st+1)
// start idx = st
// __lg(sz)
int idx = __lg(end-st+1) ;
int pow2 = 1 << idx;
int st2 = end - pow2 + 1;
return min( table[idx][st] , table[idx][st2]);
}
};
}
string s;
lli k;
int n;
vector<int> sa;
vector<int> lcp;
PrefixSumSALengths psum;
string ans;
spt::rmq rmq;
int get_lcp(int i, int j){
if(i==j){
return n - sa[i];
}
else{
return rmq.getmin(i, j-1);
}
}
int get_last(int i, int len){
// ith rank
// last guy j with lcp(i,j) >= len
assert(lcp[i] >= len);
int st = i;
int end = n-1;
int j = -1;
while(st <= end)
{
int mid = (st+end)/2;
if(get_lcp(i, mid)>=len && (mid==n-1 || get_lcp(i, mid+1)<len)){
j = mid;
break;
}
else if(get_lcp(i, mid) < len){
end = mid-1;
}
else{
st = mid+1;
}
}
assert(j!=-1);
return j;
}
void solve(){
cin>>s>>k;
n = s.size();
sa = suffix_array(s);
lcp = lcp_array(s, sa);
lcp.push_back(0);
rmq.init(lcp);
int prevlcp = 0;
for(int rank=0; rank<n; rank++){
int idx = sa[rank];
// Mistake 1 : did prevlcp instead of 'prevlcp + 1'
int stlen = prevlcp + 1;
int endlen = lcp[rank];
for(int len = stlen; len<=endlen; len++){
// string is s[idx...idx+len-1]
int lastrank = get_last(rank, len);
int cnt = lastrank - rank + 1;
if(cnt >= k){
ans = s.substr(idx, len);
// Mistake 2 : did this after the break
k = 0;
break;
}
else{
k -= cnt;
}
}
if(k==0) break;
// Mistake 3 : did not do this donelen
int donelen = max(lcp[rank], prevlcp);
int next_cnt = n - (idx + donelen) ;
assert(next_cnt >= 0);
if(next_cnt >= k){
// Mistake 4 : did not used donelen here
ans = s.substr(idx, donelen + k);
break;
}
else{
k -= next_cnt;
}
prevlcp = lcp[rank];
}
cout << ans << endl;
}
int main(){
#ifndef ONLINE_JUDGE
dm = 1;
#endif
if(!dm)fastio();
//fastio();
fileio();
lli tst=1;
//if(dm)cin>>tst ;
while(tst--){
// input
solve();
}
return 0 ;
}