#include <bits/stdc++.h>   using namespace std;   template <typename T, bool maximum_mode = false>   struct RMQ {     int n = 0, levels = 0;     vector<vector<T>> range_min;     RMQ(const vector<T> &values = {}) {       if (!values.empty()) build(values);     }     static int largest_bit(int x) { return 31 - __builtin_clz(x); }     static T better(T a, T b) { return maximum_mode ? max(a, b) : min(a, b); }     void build(const vector<T> &values) {       n = values.size();       levels = largest_bit(n) + 1;       range_min.resize(levels);       for (int k = 0; k < levels; k++) range_min[k].resize(n - (1 << k) + 1);       if (n > 0) range_min[0] = values;       for (int k = 1; k < levels; k++)         for (int i = 0; i <= n - (1 << k); i++)           range_min[k][i] =               better(range_min[k - 1][i], range_min[k - 1][i + (1 << (k - 1))]);     }     T query_value(int a, int b) const {       assert(0 <= a && a < b && b <= n);       int level = largest_bit(b - a);       return better(range_min[level][a], range_min[level][b - (1 << level)]);     }   };   template <typename T_string>   struct suffix_array {     int n = 0;     T_string str;     vector<int> suffix;     vector<int> rank;     vector<int> lcp;     RMQ<int> rmq;     suffix_array() {}     suffix_array(const T_string &_str, bool build_rmq = true) {       build(_str, build_rmq);     }     void build(const T_string &_str, bool build_rmq = true) {       str = _str;       n = str.size();       suffix.resize(n);       for (int i = 0; i < n; i++) suffix[i] = i;       bool large_alphabet = false;       for (int i = 0; i < n; i++)         if (str[i] < 0 || str[i] >= 128) large_alphabet = true;       if (large_alphabet) {         sort(suffix.begin(), suffix.end(),              [&](int a, int b) { return str[a] < str[b]; });       } else {         vector<int> freq(128, 0);         for (int i = 0; i < n; i++) freq[str[i]]++;         for (int c = 1; c < 128; c++) freq[c] += freq[c - 1];         for (int i = 0; i < n; i++) suffix[--freq[str[i]]] = i;       }       rank.resize(n);       rank[suffix[0]] = 0;       for (int i = 1; i < n; i++)         rank[suffix[i]] =             str[suffix[i]] == str[suffix[i - 1]] ? rank[suffix[i - 1]] : i;       vector<int> next_index(n);       vector<int> values(n);       bool done = false;       for (int len = 1; len < n && !done; len *= 2) {         for (int i = 0; i < n; i++) next_index[i] = i;         for (int i = n - len; i < n; i++) values[next_index[rank[i]]++] = i;         for (int i = 0; i < n; i++) {           int prev = suffix[i] - len;           if (prev >= 0) values[next_index[rank[prev]]++] = prev;         }         swap(suffix, values);         values[suffix[0]] = 0;         done = true;         for (int i = 1; i < n; i++) {           int s = suffix[i], prev = suffix[i - 1];           if (s + len < n && prev + len < n && rank[s] == rank[prev] &&               rank[s + len] == rank[prev + len]) {             values[s] = values[prev];             done = false;           } else {             values[s] = i;           }         }         swap(rank, values);       }       compute_lcp();       if (build_rmq) rmq.build(lcp);     }     void compute_lcp() {       lcp.assign(n, 0);       int match = 0;       for (int i = 0; i < n; i++) {         if (rank[i] == 0) continue;         int a = suffix[rank[i]] + match;         int b = suffix[rank[i] - 1] + match;         while (a < n && b < n && str[a++] == str[b++]) match++;         lcp[rank[i]] = match;         match = max(match - 1, 0);       }     }     int get_lcp_from_ranks(int a, int b) const {       if (a == b) return n - suffix[a];       if (a > b) swap(a, b);       return rmq.query_value(a + 1, b + 1);     }     int get_lcp(int a, int b) const {       if (a >= n || b >= n) return 0;       if (a == b) return n - a;       return get_lcp_from_ranks(rank[a], rank[b]);     }     int compare(int a, int b, int length = -1) const {       if (length < 0) length = n;       if (a == b) return 0;       int common = get_lcp(a, b);       if (common >= length) return 0;       if (a + common >= n || b + common >= n) return a + common >= n ? -1 : 1;       return str[a + common] < str[b + common]                  ? -1                  : (str[a + common] == str[b + common] ? 0 : 1);     }   };   string str;   suffix_array<string> SA;   vector<long long> prefix_sum;   int search_index(int start, int end, int i, int character) {     int low = start, high = end;     while (low < high) {       int mid = (low + high) / 2;       if (str[SA.suffix[mid] + i] >= character)         high = mid;       else         low = mid + 1;     }     return low;   }   long long compute_substring_count(int i, int start, int end) {     return prefix_sum[end] - prefix_sum[start] - (long long)i * (end - start);   }   int main() {     ios::sync_with_stdio(false);     cin.tie(nullptr);     long long K;     cin >> str >> K;     int N = str.size();     if (K > (long long)N * (N + 1) / 2) {       cout <<  No such line.  <<   n ;       return 0;     }     assert(1 <= K && K <= (long long)N * (N + 1) / 2);     SA.build(str);     prefix_sum.assign(N + 1, 0);     for (int i = 0; i < N; i++)       prefix_sum[i + 1] = prefix_sum[i] + N - SA.suffix[i];     int start = 0, end = N;     string substring =   ;     for (int i = 0; i < N; i++) {       if (SA.suffix[start] + i == N) start++;       int low = start, high = end;       while (low < high) {         int mid = (low + high) / 2;         long long additional = compute_substring_count(i, start, mid);         if (additional >= K)           high = mid;         else           low = mid + 1;       }       assert(low > start);       int c = str[SA.suffix[low - 1] + i];       substring += c;       int c_start = search_index(start, low, i, c);       K -= compute_substring_count(i, start, c_start);       start = c_start;       end = search_index(low, end, i, c + 1);       K -= end - start;       if (K <= 0) break;     }     cout << substring <<   n ;   }