Problem:
J: 接頭辞分解
問題文
英小文字からなる文字列 $S$ が与えられます。
次の条件を満たす文字列 $T$ のうち、長さが最小のものを $1$ つ求めてください。
$S$ をいくつかの部分文字列に分割し、その部分文字列すべてが $T$ の接頭辞となるようにできる。
ただし、$K$ 個の文字列の組 $Y_1, Y_2, \ldots , Y_K$ が文字列 $X$ の分割であるとは、次の条件を満たすことを言います。
$Y_1, Y_2, \ldots , Y_K$ をこの順に連結すると、 $X$ と等しくなる。
また、文字列 $x$ の接頭辞とは、 $x$ の末尾から $0$ 文字以上の文字を取り除くことで得られる文字列のことを言います。
制約
$1 \leq |S| \leq 10^5$
$S$ は英小文字からなる
入力
入力は以下の形式で標準入力から与えられる。
$S$
出力
条件を満たす文字列 $T$ のうち、長さが最小のものを $1$ つ出力せよ。
このような $T$ は複数存在するかもしれないが、どれを出力しても正答となる。
入力例1
abcaab
出力例1
abc
$S$ を "abc", "a", "ab" という $3$ つの文字列に分割します。
これらの文字列はすべて "abc" の接頭辞なので、 "abc" は条件を満たします。
また、条件を満たす文字列で長さがこれより小さいものは存在しないので、 "abc" は答えの $1$ つとなります。
入力例2
z
出力例2
z
入力例3
abracadabra
出力例3
abracad

Submitted solutions:

code_snippet: #include <bits/stdc++.h>  // clang-format off
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define each(x,v) for(auto& x : v)
#define all(v) (v).begin(),(v).end()
#define sz(v) ((int)(v).size())
#define ini(...) int __VA_ARGS__; in(__VA_ARGS__)
#define inl(...) long long __VA_ARGS__; in(__VA_ARGS__)
#define ins(...) string __VA_ARGS__; in(__VA_ARGS__)
#define inc(...) char __VA_ARGS__; in(__VA_ARGS__)
#define in2(s,t)     rep(i,sz(s)){in(s[i] , t[i]);}
#define in3(s,t,u)   rep(i,sz(s)){in(s[i] , t[i] , u[i]);}
#define in4(s,t,u,v) rep(i,sz(s)){in(s[i] , t[i] , u[i] , v[i]);}
#ifdef ONLINE_JUDGE
  #define rep(i,N) for(int i = 0; i < (int)(N); i++)
  #define repr(i,N) for(int i = (int)(N) - 1; i >= 0; i--)
  #define rep1(i,N) for(int i = 1; i <= (int)(N) ; i++)
  #define repr1(i,N) for(int i = (N) ; (int)(i) > 0 ; i--)
#else
  #define rep(i,N) for(long long i = 0; i < (long long)(N); i++)
  #define repr(i,N) for(long long i = (long long)(N) - 1; i >= 0; i--)
  #define rep1(i,N) for(long long i = 1; i <= (long long)(N) ; i++)
  #define repr1(i,N) for(long long i = (N) ; (long long)(i) > 0 ; i--)
#endif
using namespace std; void solve();
using ll = long long; template<class T = ll> using V = vector<T>;
using vi = V<int>; using vl = V<>; using vvi = V< V<int> >;
using vd = V<double>; using vs = V<string>; using vvl = V< V<> >;
constexpr int inf = 1001001001; constexpr ll infLL = (1LL << 61) - 1;
template<typename T, typename U> inline bool amin(T &x, U y) { return (y < x) ? (x = y, true) : false; }
template<typename T, typename U> inline bool amax(T &x, U y) { return (x < y) ? (x = y, true) : false; }
template<typename T,typename U>ll ceil(T a,U b){return (a + b - 1) / b;}
template<typename T, typename U> ostream& operator <<(ostream& os, const pair<T, U> &p) { os << p.first << " " << p.second; return os; }
template<typename T, typename U> istream& operator >>(istream& is, pair<T, U> &p) { is >> p.first >> p.second; return is; }
template<typename T> ostream& operator <<(ostream& os, const vector<T> &v) { int s = (int)v.size(); for(int i=0;i<s;i++) os << (i ? " " : "") << v[i]; return os; }
template<typename T> istream& operator >>(istream& is, vector<T> &v) { for(auto &x : v) is >> x; return is; }
void in(){} template <typename T,class... U> void in(T &t,U &...u){ cin >> t; in(u...);}
void out(){cout << "\n";} template <typename T,class... U> void out(const T &t,const U &...u){ cout << t; if(sizeof...(u)) cout << " "; out(u...);}
template<typename T>void die(T x){out(x); exit(0);}

#ifdef NyaanDebug
  #include "NyaanDebug.h"
  #define trc(...) do { cerr << #__VA_ARGS__ << " = "; dbg_out(__VA_ARGS__);} while(0)
  #define trca(v,N) do { cerr << #v << " = "; array_out(v , N);} while(0)
  #define trcc(v) do { cerr << "name : " << #v << "\n"; int cnt = 0; each(x , v){cerr << (cnt++) << " : "; trc(x); } } while(0)
#else
  #define trc(...)
  #define trca(...)
  #define trcc(...)
  int main(){solve();}
#endif

constexpr ll TEN(int n){ll ret=1,x=10;while(n){if(n&1)ret*=x;x*=x;n>>=1;}return ret;}
#define mem(a, val) memset(a, val, sizeof(a))

struct IoSetupNya {IoSetupNya() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(7);} } iosetupnya;
using P = pair<ll,ll>; using vp = V<P>;
constexpr int MOD = /** 1000000007; //*/ 998244353;
// clang-format on
////////////////////////////////////////////////////

// Suffix Array
//verify https://judge.yosupo.jp/submission/240
struct SuffixArray{
  int _size;
  vector<int> sa;
  string &s;
  SuffixArray(string &str):_size(str.size()) , s(str) {
    // うしさんのO( N logN )の実装
    s.push_back(0);
    sa.resize(s.size());
    iota(begin(sa), end(sa), 0);
    sort(begin(sa), end(sa), [&](int a, int b) {
      return s[a] == s[b] ? a > b : s[a] < s[b];
    });
    vector< int > classes(s.size()), c(s.begin(), s.end()), cnt(s.size());
    for(int len = 1; len < (int)s.size(); len <<= 1) {
      for(int i = 0; i < (int)s.size(); i++) {
        if(i > 0 && c[sa[i - 1]] == c[sa[i]] && sa[i - 1] + len < (int)s.size() && c[sa[i - 1] + len / 2] == c[sa[i] + len / 2]) {
          classes[sa[i]] = classes[sa[i - 1]];
        } else {
          classes[sa[i]] = i;
        }
      }
      iota(begin(cnt), end(cnt), 0);
      copy(begin(sa), end(sa), begin(c));
      for(int i = 0; i < (int)s.size(); i++) {
        int s1 = c[i] - len;
        if(s1 >= 0) sa[cnt[classes[s1]]++] = s1;
      }
      classes.swap(c);
    }
    s.pop_back();
  }
  // デバッグ用に実装
  void output() {
    cout << "SA\tidx\tstr" << endl;
    for(int i = 0; i < size(); i++) {
      cout << i << ": \t" << sa[i] << " \t" ;
      if(sa[i] != _size) cout << s.substr(sa[i],_size - sa[i]) << endl;
      else cout << "$" << endl;
    }
    cout << endl;
  }
  // sa.size()と表せると便利なので実装
  int size() const{return _size + 1;}
  // sa[]と表せると便利なのでオーバーロードしておく
  int operator[](int k) const{return sa[k]; }
};

struct LCPArray {
  const SuffixArray &SA;
  vector<int> LCP, rank;
  LCPArray(const SuffixArray &sa) : SA(sa) {
    LCP.resize(SA.size()); rank.resize(SA.size());
    // 初期化　rankはsaの逆関数
    for(int i = 0; i < SA.size(); i++) {
      rank[SA[i]] = i;
    }
    LCP[0] = 0; 
    
    // 構築
    for(int i = 0, h = 0; i < SA.size() - 1 ; i++) {
      int j = SA[rank[i] - 1] ; h ? h-- : h;
      // ここで尺取り法に近い手法を使うことでO(N)でLCPの構築をしている
      while( (i > j ? i : j) + h < SA.size() - 1 && SA.s[i + h] == SA.s[j + h] && ++h );
      LCP[rank[i] - 1] = h;
    }
  }

  // デバッグ用に実装
  void output() {
    cout << "SA\tidx\tLCP\tstr" << endl;
     for(int i = 0 ; i < SA.size() ; i++){
      cout << i << "\t" << SA[i] <<" \t" << LCP[i] << "\t"; 
      if(SA[i] == SA.size() - 1) cout << "$";
      else cout << SA.s.substr(SA[i] , SA.size() - 1 - SA[i]);
      cout << endl;
    }
  }

};

// Sparse Table
template<typename T>
struct SparseTable{
  vector< vector< T > > table;
  vector< int > log_table;

  SparseTable(const vector< T > &v) {
    int b = 0;
    while((1 << b) <= (int)v.size()) ++b;
    table.assign(b, vector< T >(1 << b));
    for(int i = 0; i < (int)v.size(); i++) {
      table[0][i] = v[i];
    }
    for(int i = 1; i < b; i++) {
      for(int j = 0; j + (1 << i) <= (1 << b); j++) {
        table[i][j] = min(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
      }
    }
    log_table.resize(v.size() + 1);
    for(int i = 2; i < (int)log_table.size(); i++) {
      log_table[i] = log_table[i >> 1] + 1;
    }
  }

  // 区間 [l , r) の最小値を返す
  inline T query(int l, int r) {
    int b = log_table[r - l];
    return min(table[b][l], table[b][r - (1 << b)]);
  }
};

// 文字列検索 検索 O(M + logN) メモリO(N logN)
// verify
// https://onlinejudge.u-aizu.ac.jp/status/users/NyaanNyaan/submissions/1/ALDS1_14_D/judge/3874273/C++14
// https://atcoder.jp/contests/abc135/submissions/7574225
// https://judge.yosupo.jp/submission/241
// https://atcoder.jp/contests/abc141/submissions/7577295
struct StringSearch{
  string &s;
  const SuffixArray &sa;
  const LCPArray &lcp;
  SparseTable<int> sparse;
  StringSearch(LCPArray &lcp)
    : s(lcp.SA.s) , sa(lcp.SA) , lcp(lcp) , sparse(lcp.LCP){ }

 // 文字列sの[i , N)と[j , N)の共通接頭辞の長さを求める
  int ArbitaryLCP(int i , int j){
    if(i == j) return (int)(s.size()) - i;
    return sparse.query(
      min(lcp.rank[i] , lcp.rank[j]) , 
      max(lcp.rank[i] , lcp.rank[j])  
    );
  }

  pair<int,int> comp(const string &t  , int len , int si , int ti = 0){
    int sn = (int)s.size() , tn = (int) t.size();
    si += len , ti += len;
    while(si < sn && ti < tn){
      if(s[si] != t[ti]) return make_pair( s[si]<t[ti] , ti);
      si++ , ti++;
    }
    return make_pair( (si>=sn && ti<tn) , ti);
  }  

  pair<int,int> find_range(int left , int med , int right , int len){
    {
      int ng = left - 1, ok = med;
      while(ng + 1 < ok){
        int cur = (ng + ok) / 2;
        if(sparse.query(cur , med) >= len) ok = cur;
        else ng = cur;
      }
      left = ok;
    }
    {
      int ok = med , ng = right + 1;
      while(ok + 1 < ng){
        int cur = (ng + ok) / 2;
        if(sparse.query(med, cur) >= len) ok = cur;
        else ng = cur;
      }
      right = ok;
    }
    return make_pair(left , right);
  }

  // 全ての出現範囲をSA上の[left , right]の範囲で返す
  // 存在しない場合は-1を返す
  pair<int,int> find(string &t){
    // 条件を満たす[left , right]を見つける
    // sa[0]は空文字列なので left = 1 とする
    // lenは既に一致している文字列の長さ
    int left = 1 , right = sa.size() - 1 , med = left;
    int leftlen = 0 , rightlen = 0 , tlen = t.size();
    pair<int,int> ret;
    while(left + 1 < right){
      med = (left + right) / 2;

      int corres_len = max(
        min(leftlen , sparse.query(left , med)) ,
        min(rightlen, sparse.query(med , right))
      );
      if(corres_len < max(leftlen , rightlen)){
        if(leftlen < rightlen) 
          left = med , leftlen = corres_len;
        else
          right= med ,rightlen = corres_len;
        continue;
      }
      ret = comp(t , corres_len , sa[med]);
      //trc(left,med,right,ret);
      if(ret.second == tlen)
        return find_range(left,med,right,tlen);
      if(ret.first == 0)
        right = med , rightlen = ret.second;
      else
        left = med , leftlen = ret.second;
    }
    if(sa.size() <= 3){
      if(comp(t,0,sa[left]).second==tlen) return find_range(left,left,right,tlen);
      if(comp(t,0,sa[right]).second==tlen) return find_range(left,right,right,tlen);
      return make_pair(-1,-1);
    }
    med = left + right - med;
    ret = comp(t , min(leftlen,rightlen) , sa[med]);
    //trc(left,med,right,ret);
    if(ret.second == tlen)
      return find_range(left,med,right,tlen);
    return make_pair(-1,-1);
  }
};

// Suffix Arrayの使い方(メモリを食うので必要なものだけ使う)
// 参照があるのでstringを削除などしないこと
/*
  SuffixArray sa(S);
  LCPArray lcp(sa);
  StringSearch search(lcp);
*/

// BIT

template< typename T >
struct BIT {
  int N; int max_2beki;

  vector< T > data;
  // 初期化 1-indexedでデータを管理する 0で初期化
  BIT(int size){
      N = ++size;
      data.assign(N, 0);
      max_2beki = 1;
      while(max_2beki * 2 <= N) max_2beki *= 2;
  }

  // [0,k](閉区間)の総和 閉区間に注意！
  T sum(int k) {
    if(k < 0) return 0; // k<0のとき0を返す
    T ret = 0;
    for(++k; k > 0; k -= k & -k) ret += data[k];
    return (ret);
  }

  // [l,r](閉区間)の総和
  inline T sum(int l,int r){
    return sum(r) - sum(l-1);
  }

  // 一点取得 更新はできないことに注意
  inline T operator[](int k){
    return sum(k) - sum(k-1);
  }

  // data[k] += x;
  void add(int k, T x) {
    for(++k; k < N; k += k & -k) data[k] += x;
  }

  // imos法 [l,r]にxを加算
  void imos(int l,int r,T x){
    add(l , x); add(r + 1 , -x);
  }

  // lower_bound sum(i)がval以上となる最小のi
  int lower_bound(T w){
    if(w <= 0) return 0;
    int x = 0;
    for(int k = max_2beki; k > 0; k /= 2){
      if(x+k <= N - 1 && data[x + k] < w){
        w -= data[x + k];
        x += k;
      }
    }
    return x;
  }

  // upper_bound sum(i)がvalより大きくなる最小のi
  int upper_bound(T w){
    if(w < 0) return 0;
    int x = 0;
    for(int k = max_2beki; k > 0; k /= 2){
      if(x+k <= N - 1 && data[x + k] <= w){
        w -= data[x + k];
        x += k;
      }
    }
    return x;
  }

};

void solve(){
 ins(S);
  SuffixArray sa(S);
  LCPArray lcp(sa);
  StringSearch search(lcp);
  trc(S);
  if(sz(S)==1){out(S);return;}
  
  int N = sz(S);
  int left = 0 , len = 1;
  int nxt = 1;
  BIT<ll> bit(N + 10);
    
  if(sz(S) <= 100){
      // 愚直
  int ans = N , al = 0 , alen = N;
  rep(i , N) rep1(j , N){
    int med = j - i;
    if(med <= 0) continue;
    // [left , left + med)を走査
    fill(all(bit.data) , 0);
    //trc(med);
    rep(k , N){
      int x = search.ArbitaryLCP(i , k);
      //trc(i , x); 
      x = min(x , med);
      bit.imos(k , k + x - 1 , 1);
    }
    //rep(i,N) trc(bit.sum(i));
    int flg = 1;
    rep(i , N){
      flg &= bit.sum(i) != 0;
    }
    if( (flg == 1) && amin(ans , med) ){
      trc(flg , i, j);
      al = i , alen = med;
    }
  }
  
    out(S.substr(al , alen));
    return ;
  }    
  // [left , left + len)が本質 
  while(1){
    //trc(left , len);
    int x = search.ArbitaryLCP(left , nxt);
    if(x == 0){
      len++;
      nxt = left + len;
      if(nxt == N) break;
    }
    else if(x >= len){
      left = nxt;
      if(left + len >= N) break;
      //if(S[left + len] != S[left]) len++;
      nxt = left + len;
    }
    else{
      if(x + nxt >= N) break;
      nxt++;
    }
  }

  trc(left , len , S.substr(left , len));
  int ng = 0 , ok = len;
  while(ng + 1 < ok){
    int med = (ng + ok) / 2;
    // [left , left + med)を走査
    fill(all(bit.data) , 0);
    //trc(med);
    rep(i , N){
      int x = search.ArbitaryLCP(left , i);
      //trc(i , x);
      x = min(x , med);
      bit.imos(i , i + x - 1 , 1);
    }
    //rep(i,N) trc(bit.sum(i));
    int flg = 1;
    rep(i , N){
      flg &= bit.sum(i) != 0;
    }
    (flg ? ok : ng) = med;
  }
  
  out(S.substr(left , ok));
  
  /*
  // 愚直
  int ans = N , al = 0 , alen = N;
  rep(i , N) rep1(j , N){
    int med = j - i;
    if(med <= 0) continue;
    // [left , left + med)を走査
    fill(all(bit.data) , 0);
    //trc(med);
    rep(k , N){
      int x = search.ArbitaryLCP(i , k);
      //trc(i , x); 
      x = min(x , med);
      bit.imos(k , k + x - 1 , 1);
    }
    //rep(i,N) trc(bit.sum(i));
    int flg = 1;
    rep(i , N){
      flg &= bit.sum(i) != 0;
    }
    if( (flg == 1) && amin(ans , med) ){
      trc(flg , i, j);
      al = i , alen = med;
    }
  }

  
  if(S.substr(left , ok) != S.substr(al ,alen)){
    trc(S);
    trc(S.substr(left , ok));
    trc(S.substr(al , alen));
    exit(1);
  }
  */


}
accuracy: 0.21929824561403508
time_ms: 100
memory_kb: 14060
final_rank: 4

code_snippet: #include <bits/stdc++.h>  // clang-format off
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define each(x,v) for(auto& x : v)
#define all(v) (v).begin(),(v).end()
#define sz(v) ((int)(v).size())
#define ini(...) int __VA_ARGS__; in(__VA_ARGS__)
#define inl(...) long long __VA_ARGS__; in(__VA_ARGS__)
#define ins(...) string __VA_ARGS__; in(__VA_ARGS__)
#define inc(...) char __VA_ARGS__; in(__VA_ARGS__)
#define in2(s,t)     rep(i,sz(s)){in(s[i] , t[i]);}
#define in3(s,t,u)   rep(i,sz(s)){in(s[i] , t[i] , u[i]);}
#define in4(s,t,u,v) rep(i,sz(s)){in(s[i] , t[i] , u[i] , v[i]);}
#ifdef ONLINE_JUDGE
  #define rep(i,N) for(int i = 0; i < (int)(N); i++)
  #define repr(i,N) for(int i = (int)(N) - 1; i >= 0; i--)
  #define rep1(i,N) for(int i = 1; i <= (int)(N) ; i++)
  #define repr1(i,N) for(int i = (N) ; (int)(i) > 0 ; i--)
#else
  #define rep(i,N) for(long long i = 0; i < (long long)(N); i++)
  #define repr(i,N) for(long long i = (long long)(N) - 1; i >= 0; i--)
  #define rep1(i,N) for(long long i = 1; i <= (long long)(N) ; i++)
  #define repr1(i,N) for(long long i = (N) ; (long long)(i) > 0 ; i--)
#endif
using namespace std; void solve();
using ll = long long; template<class T = ll> using V = vector<T>;
using vi = V<int>; using vl = V<>; using vvi = V< V<int> >;
using vd = V<double>; using vs = V<string>; using vvl = V< V<> >;
constexpr int inf = 1001001001; constexpr ll infLL = (1LL << 61) - 1;
template<typename T, typename U> inline bool amin(T &x, U y) { return (y < x) ? (x = y, true) : false; }
template<typename T, typename U> inline bool amax(T &x, U y) { return (x < y) ? (x = y, true) : false; }
template<typename T,typename U>ll ceil(T a,U b){return (a + b - 1) / b;}
template<typename T, typename U> ostream& operator <<(ostream& os, const pair<T, U> &p) { os << p.first << " " << p.second; return os; }
template<typename T, typename U> istream& operator >>(istream& is, pair<T, U> &p) { is >> p.first >> p.second; return is; }
template<typename T> ostream& operator <<(ostream& os, const vector<T> &v) { int s = (int)v.size(); for(int i=0;i<s;i++) os << (i ? " " : "") << v[i]; return os; }
template<typename T> istream& operator >>(istream& is, vector<T> &v) { for(auto &x : v) is >> x; return is; }
void in(){} template <typename T,class... U> void in(T &t,U &...u){ cin >> t; in(u...);}
void out(){cout << "\n";} template <typename T,class... U> void out(const T &t,const U &...u){ cout << t; if(sizeof...(u)) cout << " "; out(u...);}
template<typename T>void die(T x){out(x); exit(0);}

#ifdef NyaanDebug
  #include "NyaanDebug.h"
  #define trc(...) do { cerr << #__VA_ARGS__ << " = "; dbg_out(__VA_ARGS__);} while(0)
  #define trca(v,N) do { cerr << #v << " = "; array_out(v , N);} while(0)
  #define trcc(v) do { cerr << "name : " << #v << "\n"; int cnt = 0; each(x , v){cerr << (cnt++) << " : "; trc(x); } } while(0)
#else
  #define trc(...)
  #define trca(...)
  #define trcc(...)
  int main(){solve();}
#endif

constexpr ll TEN(int n){ll ret=1,x=10;while(n){if(n&1)ret*=x;x*=x;n>>=1;}return ret;}
#define mem(a, val) memset(a, val, sizeof(a))

struct IoSetupNya {IoSetupNya() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(7);} } iosetupnya;
using P = pair<ll,ll>; using vp = V<P>;
constexpr int MOD = /** 1000000007; //*/ 998244353;
// clang-format on
////////////////////////////////////////////////////

// Suffix Array
//verify https://judge.yosupo.jp/submission/240
struct SuffixArray{
  int _size;
  vector<int> sa;
  string &s;
  SuffixArray(string &str):_size(str.size()) , s(str) {
    // うしさんのO( N logN )の実装
    s.push_back(0);
    sa.resize(s.size());
    iota(begin(sa), end(sa), 0);
    sort(begin(sa), end(sa), [&](int a, int b) {
      return s[a] == s[b] ? a > b : s[a] < s[b];
    });
    vector< int > classes(s.size()), c(s.begin(), s.end()), cnt(s.size());
    for(int len = 1; len < (int)s.size(); len <<= 1) {
      for(int i = 0; i < (int)s.size(); i++) {
        if(i > 0 && c[sa[i - 1]] == c[sa[i]] && sa[i - 1] + len < (int)s.size() && c[sa[i - 1] + len / 2] == c[sa[i] + len / 2]) {
          classes[sa[i]] = classes[sa[i - 1]];
        } else {
          classes[sa[i]] = i;
        }
      }
      iota(begin(cnt), end(cnt), 0);
      copy(begin(sa), end(sa), begin(c));
      for(int i = 0; i < (int)s.size(); i++) {
        int s1 = c[i] - len;
        if(s1 >= 0) sa[cnt[classes[s1]]++] = s1;
      }
      classes.swap(c);
    }
    s.pop_back();
  }
  // デバッグ用に実装
  void output() {
    cout << "SA\tidx\tstr" << endl;
    for(int i = 0; i < size(); i++) {
      cout << i << ": \t" << sa[i] << " \t" ;
      if(sa[i] != _size) cout << s.substr(sa[i],_size - sa[i]) << endl;
      else cout << "$" << endl;
    }
    cout << endl;
  }
  // sa.size()と表せると便利なので実装
  int size() const{return _size + 1;}
  // sa[]と表せると便利なのでオーバーロードしておく
  int operator[](int k) const{return sa[k]; }
};

struct LCPArray {
  const SuffixArray &SA;
  vector<int> LCP, rank;
  LCPArray(const SuffixArray &sa) : SA(sa) {
    LCP.resize(SA.size()); rank.resize(SA.size());
    // 初期化　rankはsaの逆関数
    for(int i = 0; i < SA.size(); i++) {
      rank[SA[i]] = i;
    }
    LCP[0] = 0; 
    
    // 構築
    for(int i = 0, h = 0; i < SA.size() - 1 ; i++) {
      int j = SA[rank[i] - 1] ; h ? h-- : h;
      // ここで尺取り法に近い手法を使うことでO(N)でLCPの構築をしている
      while( (i > j ? i : j) + h < SA.size() - 1 && SA.s[i + h] == SA.s[j + h] && ++h );
      LCP[rank[i] - 1] = h;
    }
  }

  // デバッグ用に実装
  void output() {
    cout << "SA\tidx\tLCP\tstr" << endl;
     for(int i = 0 ; i < SA.size() ; i++){
      cout << i << "\t" << SA[i] <<" \t" << LCP[i] << "\t"; 
      if(SA[i] == SA.size() - 1) cout << "$";
      else cout << SA.s.substr(SA[i] , SA.size() - 1 - SA[i]);
      cout << endl;
    }
  }

};

// Sparse Table
template<typename T>
struct SparseTable{
  vector< vector< T > > table;
  vector< int > log_table;

  SparseTable(const vector< T > &v) {
    int b = 0;
    while((1 << b) <= (int)v.size()) ++b;
    table.assign(b, vector< T >(1 << b));
    for(int i = 0; i < (int)v.size(); i++) {
      table[0][i] = v[i];
    }
    for(int i = 1; i < b; i++) {
      for(int j = 0; j + (1 << i) <= (1 << b); j++) {
        table[i][j] = min(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
      }
    }
    log_table.resize(v.size() + 1);
    for(int i = 2; i < (int)log_table.size(); i++) {
      log_table[i] = log_table[i >> 1] + 1;
    }
  }

  // 区間 [l , r) の最小値を返す
  inline T query(int l, int r) {
    int b = log_table[r - l];
    return min(table[b][l], table[b][r - (1 << b)]);
  }
};

// 文字列検索 検索 O(M + logN) メモリO(N logN)
// verify
// https://onlinejudge.u-aizu.ac.jp/status/users/NyaanNyaan/submissions/1/ALDS1_14_D/judge/3874273/C++14
// https://atcoder.jp/contests/abc135/submissions/7574225
// https://judge.yosupo.jp/submission/241
// https://atcoder.jp/contests/abc141/submissions/7577295
struct StringSearch{
  string &s;
  const SuffixArray &sa;
  const LCPArray &lcp;
  SparseTable<int> sparse;
  StringSearch(LCPArray &lcp)
    : s(lcp.SA.s) , sa(lcp.SA) , lcp(lcp) , sparse(lcp.LCP){ }

 // 文字列sの[i , N)と[j , N)の共通接頭辞の長さを求める
  int ArbitaryLCP(int i , int j){
    if(i == j) return (int)(s.size()) - i;
    return sparse.query(
      min(lcp.rank[i] , lcp.rank[j]) , 
      max(lcp.rank[i] , lcp.rank[j])  
    );
  }

  pair<int,int> comp(const string &t  , int len , int si , int ti = 0){
    int sn = (int)s.size() , tn = (int) t.size();
    si += len , ti += len;
    while(si < sn && ti < tn){
      if(s[si] != t[ti]) return make_pair( s[si]<t[ti] , ti);
      si++ , ti++;
    }
    return make_pair( (si>=sn && ti<tn) , ti);
  }  

  pair<int,int> find_range(int left , int med , int right , int len){
    {
      int ng = left - 1, ok = med;
      while(ng + 1 < ok){
        int cur = (ng + ok) / 2;
        if(sparse.query(cur , med) >= len) ok = cur;
        else ng = cur;
      }
      left = ok;
    }
    {
      int ok = med , ng = right + 1;
      while(ok + 1 < ng){
        int cur = (ng + ok) / 2;
        if(sparse.query(med, cur) >= len) ok = cur;
        else ng = cur;
      }
      right = ok;
    }
    return make_pair(left , right);
  }

  // 全ての出現範囲をSA上の[left , right]の範囲で返す
  // 存在しない場合は-1を返す
  pair<int,int> find(string &t){
    // 条件を満たす[left , right]を見つける
    // sa[0]は空文字列なので left = 1 とする
    // lenは既に一致している文字列の長さ
    int left = 1 , right = sa.size() - 1 , med = left;
    int leftlen = 0 , rightlen = 0 , tlen = t.size();
    pair<int,int> ret;
    while(left + 1 < right){
      med = (left + right) / 2;

      int corres_len = max(
        min(leftlen , sparse.query(left , med)) ,
        min(rightlen, sparse.query(med , right))
      );
      if(corres_len < max(leftlen , rightlen)){
        if(leftlen < rightlen) 
          left = med , leftlen = corres_len;
        else
          right= med ,rightlen = corres_len;
        continue;
      }
      ret = comp(t , corres_len , sa[med]);
      //trc(left,med,right,ret);
      if(ret.second == tlen)
        return find_range(left,med,right,tlen);
      if(ret.first == 0)
        right = med , rightlen = ret.second;
      else
        left = med , leftlen = ret.second;
    }
    if(sa.size() <= 3){
      if(comp(t,0,sa[left]).second==tlen) return find_range(left,left,right,tlen);
      if(comp(t,0,sa[right]).second==tlen) return find_range(left,right,right,tlen);
      return make_pair(-1,-1);
    }
    med = left + right - med;
    ret = comp(t , min(leftlen,rightlen) , sa[med]);
    //trc(left,med,right,ret);
    if(ret.second == tlen)
      return find_range(left,med,right,tlen);
    return make_pair(-1,-1);
  }
};

// Suffix Arrayの使い方(メモリを食うので必要なものだけ使う)
// 参照があるのでstringを削除などしないこと
/*
  SuffixArray sa(S);
  LCPArray lcp(sa);
  StringSearch search(lcp);
*/

// BIT

template< typename T >
struct BIT {
  int N; int max_2beki;

  vector< T > data;
  // 初期化 1-indexedでデータを管理する 0で初期化
  BIT(int size){
      N = ++size;
      data.assign(N, 0);
      max_2beki = 1;
      while(max_2beki * 2 <= N) max_2beki *= 2;
  }

  // [0,k](閉区間)の総和 閉区間に注意！
  T sum(int k) {
    if(k < 0) return 0; // k<0のとき0を返す
    T ret = 0;
    for(++k; k > 0; k -= k & -k) ret += data[k];
    return (ret);
  }

  // [l,r](閉区間)の総和
  inline T sum(int l,int r){
    return sum(r) - sum(l-1);
  }

  // 一点取得 更新はできないことに注意
  inline T operator[](int k){
    return sum(k) - sum(k-1);
  }

  // data[k] += x;
  void add(int k, T x) {
    for(++k; k < N; k += k & -k) data[k] += x;
  }

  // imos法 [l,r]にxを加算
  void imos(int l,int r,T x){
    add(l , x); add(r + 1 , -x);
  }

  // lower_bound sum(i)がval以上となる最小のi
  int lower_bound(T w){
    if(w <= 0) return 0;
    int x = 0;
    for(int k = max_2beki; k > 0; k /= 2){
      if(x+k <= N - 1 && data[x + k] < w){
        w -= data[x + k];
        x += k;
      }
    }
    return x;
  }

  // upper_bound sum(i)がvalより大きくなる最小のi
  int upper_bound(T w){
    if(w < 0) return 0;
    int x = 0;
    for(int k = max_2beki; k > 0; k /= 2){
      if(x+k <= N - 1 && data[x + k] <= w){
        w -= data[x + k];
        x += k;
      }
    }
    return x;
  }

};

void solve(){
 ins(S);
  SuffixArray sa(S);
  LCPArray lcp(sa);
  StringSearch search(lcp);
  trc(S);
  if(sz(S)==1){out(S);return;}
  
  int N = sz(S);
  int left = 0 , len = 1;
  int nxt = 1;
  BIT<ll> bit(N + 10);
    
  if(sz(S) <= 100){
      // 愚直
  int ans = N , al = 0 , alen = N;
  rep(i , N) rep1(j , N){
    int med = j - i;
    if(med <= 0) continue;
    // [left , left + med)を走査
    fill(all(bit.data) , 0);
    //trc(med);
    rep(k , N){
      int x = search.ArbitaryLCP(i , k);
      //trc(i , x); 
      x = min(x , med);
      bit.imos(k , k + x - 1 , 1);
    }
    //rep(i,N) trc(bit.sum(i));
    int flg = 1;
    rep(i , N){
      flg &= bit.sum(i) != 0;
    }
    if( (flg == 1) && amin(ans , med) ){
      trc(flg , i, j);
      al = i , alen = med;
    }
  }
  
    out(S.substr(al , alen));
    return ;
  }    
  // [left , left + len)が本質 
  while(1){
    //trc(left , len);
    int x = search.ArbitaryLCP(left , nxt);
    if(x == 0){
      len++;
      nxt = left + len;
      if(nxt == N) break;
    }
    else if(x >= len){
      left = nxt;
      if(left + len >= N) break;
      //if(S[left + len] != S[left]) len++;
      nxt = left + len;
    }
    else{
      if(x + nxt >= N) break;
      nxt = x + nxt;
    }
  }

  trc(left , len , S.substr(left , len));
  int ng = 0 , ok = len;
  while(ng + 1 < ok){
    int med = (ng + ok) / 2;
    // [left , left + med)を走査
    fill(all(bit.data) , 0);
    //trc(med);
    rep(i , N){
      int x = search.ArbitaryLCP(left , i);
      //trc(i , x);
      x = min(x , med);
      bit.imos(i , i + x - 1 , 1);
    }
    //rep(i,N) trc(bit.sum(i));
    int flg = 1;
    rep(i , N){
      flg &= bit.sum(i) != 0;
    }
    (flg ? ok : ng) = med;
  }
  
  out(S.substr(left , ok));
  
  /*
  // 愚直
  int ans = N , al = 0 , alen = N;
  rep(i , N) rep1(j , N){
    int med = j - i;
    if(med <= 0) continue;
    // [left , left + med)を走査
    fill(all(bit.data) , 0);
    //trc(med);
    rep(k , N){
      int x = search.ArbitaryLCP(i , k);
      //trc(i , x); 
      x = min(x , med);
      bit.imos(k , k + x - 1 , 1);
    }
    //rep(i,N) trc(bit.sum(i));
    int flg = 1;
    rep(i , N){
      flg &= bit.sum(i) != 0;
    }
    if( (flg == 1) && amin(ans , med) ){
      trc(flg , i, j);
      al = i , alen = med;
    }
  }

  
  if(S.substr(left , ok) != S.substr(al ,alen)){
    trc(S);
    trc(S.substr(left , ok));
    trc(S.substr(al , alen));
    exit(1);
  }
  */


}
accuracy: 0.24561403508771928
time_ms: 100
memory_kb: 14156
final_rank: 3

code_snippet: // vvvvvvvvvvvv TEMPLATE vvvvvvvvvvvv
#include <bits/stdc++.h>
using namespace std; using ll = long long; using P = pair<ll, ll>;
const ll linf = 1e18; const double eps = 1e-12, pi = acos(-1);
#define FOR(i,a,b) for (ll i=(a),__last_##i=(b);i<__last_##i;i++)
#define RFOR(i,a,b) for (ll i=(b)-1,__last_##i=(a);i>=__last_##i;i--)
#define REP(i,n) FOR(i,0,n)
#define RREP(i,n) RFOR(i,0,n)
#define __GET_MACRO3(_1, _2, _3, NAME, ...) NAME
#define each(i,a) for (auto&& i : a)
#define rep(...) __GET_MACRO3(__VA_ARGS__, FOR, REP)(__VA_ARGS__)
#define rrep(...) __GET_MACRO3(__VA_ARGS__, RFOR, RREP)(__VA_ARGS__)
#define pb push_back
#define eb emplace_back
#define all(a) begin(a),end(a)
#define chmin(x,v) x = min(x, v)
#define chmax(x,v) x = max(x, v)
#define min(x,y) (x < y ? x : y)
#define max(x,y) (x < y ? y : x)
template<typename Head> void out(Head h) { cout << h << endl; } template<typename Head, typename... Tail>void out(Head h, Tail... t) { cout << h << " "; out(t...); }
template<typename T> istream& operator>>(istream& is, vector<T>& v) { each(x,v) is >> x; return is; }
template<typename T> ostream& operator<<(ostream& os, const vector<T>& v) { rep(i,v.size()) { if (i) os << " "; os << v[i]; } return os; }
template<typename T> ostream& operator<<(ostream& os, const vector<string>& v) { rep(i,v.size()) { if (i) os << endl; os << v[i]; } return os; }
template<typename T> ostream& operator<<(ostream& os, const vector<vector<T>>& v) { rep(i,v.size()) { if (i) os << endl; os << v[i]; } return os; }
struct yes_no : std::numpunct<char> { string_type do_truename() const { return "Yes"; } string_type do_falsename() const { return "No"; } };
void solve(); int main() {
  ios::sync_with_stdio(false); cin.tie(0); locale loc(locale(), new yes_no); cout.imbue(loc); cout << fixed << setprecision(10) << boolalpha;
  solve();
}
// ^^^^^^^^^^^^ TEMPLATE ^^^^^^^^^^^^

ll mul(ll a, ll b, ll mod) {
  return a * b % mod;
}
ll add(ll a, ll b, ll mod) {
  return (a + b) % mod;
}
ll sub(ll a, ll b, ll mod) {
  return (a - b + mod) % mod;
}
ll power(ll x, ll n, ll mod) {
  ll res = 1;
  for (ll i = 1; i <= n; i <<= 1) {
    if (i & n) res = mul(res, x, mod);
    x = mul(x, x, mod);
  }
  return res;
}
ll inv(ll n, ll mod) {
  return power(n, mod-2, mod);
}

template<class String>
class SuffixArray {
  const ll n;
  const String str;
  vector<ll> sa, lcp;
public:
  SuffixArray(const String& s) : str(s), n(s.size()) {}
  // sa: {s.substr(sa[0]), ..., s.substr(sa[n])} is sorted
  // sa[0] = n
  vector<ll> make_sa() {
    sa.assign(n+1, 0);
    rep(i, n+1) sa[i] = i;
    vector<ll> rank(all(str));
    rank.pb(-1);
    auto f = [&](ll idx, ll len) {
      return idx + len <= n ? rank[idx+len] : -1;
    };
    for (ll k = 1; k <= n; k <<= 1) {
      auto compare = [&](ll a, ll b) {
        if (rank[a] != rank[b]) return rank[a] < rank[b];
        else return f(a, k) < f(b, k);
      };
      sort(all(sa), compare);
      vector<ll> nrank(n+1, 0);
      rep(i, 1, n+1) {
        nrank[sa[i]] = nrank[sa[i-1]] + compare(sa[i-1], sa[i]);
      }
      rank = nrank;
    }
    return sa;
  }
  // lcp: lcp[i] = lcp(s.substr(sa[i]), s.substr(sa[i+1])
  // lcp[n] = 0
  vector<ll> make_lcp() {
    assert(sa.size() > 0);
    lcp.assign(sa.size(), 0);
    vector<ll> rank(n+1);
    rep(i, n+1) rank[sa[i]] = i;
    ll h = 0;
    rep(i, n) {
      if (h > 0) --h;
      assert(rank[i] > 0);
      for (ll j = sa[rank[i]-1]; j + h < n && i + h < n; h++) {
        if (str[j+h] != str[i+h]) break;
      }
      lcp[rank[i]-1] = h;
    }
    return lcp;
  }
  vector<ll> search(const String& s) {
    // assert(lcp.size() > 0);
    ll l = -1, r = -1;
    {
      ll lb = 0, ub = n+1;
      while (ub - lb > 1) {
        ll mid = (lb + ub) / 2;
        if (str.substr(sa[mid], s.size()) >= s) {
          ub = mid;
        }
        else {
          lb = mid;
        }
      }
      l = ub;
    }
    {
      ll lb = 0, ub = n+1;
      while (ub - lb > 1) {
        ll mid = (lb + ub) / 2;
        if (str.substr(sa[mid], s.size()) > s) {
          ub = mid;
        }
        else {
          lb = mid;
        }
      }
      r = ub;
    }
    vector<ll> res;
    if (str.substr(sa[l], s.size()) == s) {
      rep(i, l, r) {
        res.pb(sa[i]);
      }
    }
    return res;
  }
};

template <class Monoid>
class SegmentTree {
  using T = typename Monoid::type;
  const int size_, n;
  std::vector<T> data;
  int expand(int m) const { return m <= 1 ? 1 : expand((m + 1) / 2) * 2; }
public:
  SegmentTree() : SegmentTree(0) {}
  SegmentTree(const std::vector<T> &vec) :
    size_(vec.size()), n(expand(size_)), data(n * 2, Monoid::id()) {
    std::copy(begin(vec), end(vec), begin(data) + n);
    for (int i = n - 1; i >= 0; --i) {
      data[i] = Monoid::op(data[i * 2 + 0], data[i * 2 + 1]);
    }
  }
  SegmentTree(const int count, const T &value = Monoid::id()) :
    SegmentTree(std::vector<T>(count, value)) {}
  int size() const { return size_; }
  void update(int pos, const T &value) {
    assert (0 <= pos && pos < size_); // assertion
    data[pos += n] = value;
    while (pos /= 2) {
      data[pos] = Monoid::op(data[pos * 2], data[pos * 2 + 1]);
    }
  }
  T find(int l, int r) const {
    assert (0 <= l && l <= r && r <= size_); // assertion
    l += n; r += n;
    T res1 = Monoid::id(), res2 = Monoid::id();
    while (l != r) {
      if (l % 2) res1 = Monoid::op(res1, data[l++]);
      if (r % 2) res2 = Monoid::op(data[--r], res2);
      l /= 2; r /= 2;
    }
    return Monoid::op(res1, res2);
  }
  T operator[](size_t pos) {
    return data[n+pos];
  }
  using value_type = T;
  using update_type = T;
};

struct Min {
  using type = ll;
  static type id() { return linf; }
  static type op(const type &l, const type &r) { return min(l, r); }
};
struct Max {
  using type = ll;
  static type id() { return -linf; }
  static type op(const type &l, const type &r) { return max(l, r); }
};
struct Sum {
  using type = ll;
  static type id() { return 0; }
  static type op(const type &l, const type &r) { return l + r; }
};

void solve() {
  string s; cin >> s;
  const ll n = s.size();
  SuffixArray<string> suffix_array(s);
  vector<ll> sa = suffix_array.make_sa();
  vector<ll> lcp = suffix_array.make_lcp();
  vector<ll> sa_r(n+1);
  rep(i, n+1) sa_r[sa[i]] = i;
  SegmentTree<Min> seg(lcp);
  set<ll> sa_rs;
  sa_rs.insert(sa_r[0]);
  ll mx = 1;
  ll pos = 0, len = 1;
  rep(i, 1, n) {
    // cout << "i = " << i << endl;
    sa_rs.insert(sa_r[i]);
    ll l = sa_r[i], r = sa_r[pos];
    if (l > r) swap(l, r);
    mx = max(mx, i + min(len, seg.find(l, r)));
    if (seg.find(l, r) >= len) {
      pos = i;
    }
    if (mx <= i) {
      mx = i+1;
      ll nlen = i+1 - pos;
      // cout << pos << " " << s.substr(pos, nlen) << endl;
      auto it0 = sa_rs.lower_bound(sa_r[pos]);
      {
        auto it = it0;
        ll h = nlen;
        while (h > len) {
          ll l = *it;
          ++it;
          if (it == sa_rs.end()) break;
          ll r = *it;
          chmin(h, seg.find(l, r));
          chmax(mx, sa[r] + h);
        }
      }
      {
        // cout << "left" << endl;
        auto it = it0;
        ll h = nlen;
        while (it != sa_rs.begin() && h > len) {
          ll r = *it;
          --it;
          ll l = *it;
          chmin(h, seg.find(l, r));
          // cout << l << " " << s.substr(sa[l]) << " " << h << endl;
          chmax(mx, sa[l] + h);
        }
      }
      len = nlen;
    }
  }
  cout << s.substr(pos, len) << endl;
}
accuracy: 1
time_ms: 100
memory_kb: 13696
final_rank: 1

code_snippet: #include <bits/stdc++.h>

using namespace std;

void suffixArray(const string &s, vector<int> &sa, vector<int> &rank){
    int n = s.size();
    sa = vector<int>(n);
    rank = vector<int>(n);
    iota(sa.begin(), sa.end(), 0);
    sort(sa.begin(), sa.end(), [&](const int l, const int r){
        return s[l] < s[r];
    });
    for(int i = 0; i < n; i++) rank[i] = s[i] - 'a';
    for(int k = 1; k < n; k <<= 1){
        auto comp = [&](const int l, const int r){
            if(rank[l] != rank[r]) return rank[l] < rank[r];
            return (l + k < n ? rank[l + k] : -1) < (r + k < n ? rank[r + k] : -1);
        };
        sort(sa.begin(), sa.end(), comp);
        vector<int> tmp = rank;
        tmp[sa[0]] = 0;
        for(int i = 1; i < n; i++) tmp[sa[i]] = tmp[sa[i - 1]] + (comp(sa[i - 1], sa[i]) ? 1 : 0);
        rank = tmp;
    }
}

void longestCommonPrefix(const string &s, const vector<int> &sa, const vector<int> &rank, vector<int> &lcp){
    int n = s.size();
    lcp = vector<int>(n);
    int h = 0;
    for(int i = 0; i < n; i++){
        if(h > 0) h--;
        if(rank[i] == 0) continue;
        int j = sa[rank[i] - 1];
        while(i + h < n && j + h < n){
            if(s[i + h] != s[j + h]) break;
            h++;
        }
        lcp[rank[i] - 1] = h;
    }
}

const int inf = 1e9;

struct SegmentTree{
    int n;
    vector<int> lcp;
    vector<int> range;

    SegmentTree(vector<int> &v){
        int sz = v.size();
        n = 1;
        while(n < sz) n <<= 1;
        lcp.resize(2 * n - 1, 0);
        range.resize(2 * n - 1, 0);
        for(int i = 0; i < sz; i++) lcp[i + n - 1] = v[i];
        for(int i = n - 2; i >= 0; i--) lcp[i] = min(lcp[i * 2 + 1], lcp[i * 2 + 2]);
    }

    int pl = 1;

    int getlcp(int a, int b, int k = 0, int l = 0, int r = -1){
        if(r < 0) r = n;
        if(r <= a || b <= l) return inf;
        if(a <= l && r <= b) return lcp[k];
        int xl = getlcp(a, b, 2 * k + 1, l, (l + r) / 2);
        int xr = getlcp(a, b, 2 * k + 2, (l + r) / 2, r);
        return min(xl, xr);
    }

    void update(int a, int b, int x, int k = 0, int l = 0, int r = -1){
        if(r < 0) r = n;
        if(r <= a || b <= l) return;
        if(a <= l && r <= b){
            range[k] = x;
            return;
        }
        update(a, b, x, 2 * k + 1, l, (l + r) / 2);
        update(a, b, x, 2 * k + 2, (l + r) / 2, r);
    }

    void update(int pos, int len){
        for(; pl <= len; pl++){
            int l1 = -1, r1 = pos;
            while(r1 - l1 > 1){
                int mid = (l1 + r1) / 2;
                if(getlcp(mid, pos) >= pl){
                    r1 = mid;
                }else{
                    l1 = mid;
                }
            }
            int l2 = pos, r2 = n;
            while(r2 - l2 > 1){
                int mid = (l2 + r2) / 2;
                if(getlcp(pos, mid) >= pl){
                    l2 = mid;
                }else{
                    r2 = mid;
                }
            }
            update(r1, r2, pl);
        }
    }

    int get(int k){
        k += n - 1;
        int ans = range[k];
        while(k){
            k = (k - 1) / 2;
            ans = max(ans, range[k]);
        }
        return ans;
    }
};

int main(){
    string s;
    cin >> s;
    int n = s.size();

    vector<int> sa, rank, lcp;
    suffixArray(s, sa, rank);
    longestCommonPrefix(s, sa, rank, lcp);

    SegmentTree seg(lcp);
    int can = -1;
    int pos = 0;
    int len = 0;
    for(int i = 0; i < n; i++){
        int range = seg.get(rank[i]);
        if(range == len) pos = i;
        can = max(can, i + range - 1);
        if(can < i){
            len = i - pos + 1;
            seg.update(rank[pos], len);
        }
    }
    cout << s.substr(pos, len) << endl;
}
accuracy: 1
time_ms: 370
memory_kb: 6488
final_rank: 2