#include #include #include #include #include #include #include #include #include #include #include #include using namespace std; // Templatex from KACTL using vi = vector; #define rep(i, a, b) for(int i = a; i < (b); ++i) #define all(x) begin(x), end(x) #define sz(x) (int)(x).size() struct SuffixArray { vi sa, lcp; SuffixArray(string& s, int lim=256) { // or basic_string int n = sz(s) + 1, k = 0, a, b; vi x(all(s)+1), y(n), ws(max(n, lim)), rank(n); sa = lcp = y, iota(all(sa), 0); for (int j = 0, p = 0; p < n; j = max(1, j * 2), lim = p) { p = j, iota(all(y), n - j); rep(i,0,n) if (sa[i] >= j) y[p++] = sa[i] - j; fill(all(ws), 0); rep(i,0,n) ws[x[i]]++; rep(i,1,lim) ws[i] += ws[i - 1]; for (int i = n; i--;) sa[--ws[x[y[i]]]] = y[i]; swap(x, y), p = 1, x[sa[0]] = 0; rep(i,1,n) a = sa[i - 1], b = sa[i], x[b] = (y[a] == y[b] && y[a + j] == y[b + j]) ? p - 1 : p++; } rep(i,1,n) rank[sa[i]] = i; for (int i = 0, j; i < n - 1; lcp[rank[i++]] = k) for (k && k--, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k++); } }; template struct WaveletTree { vector build_idxs(size_t n) { vector _idxs(n); iota(_idxs.begin(), _idxs.end(), 0); return _idxs; } WaveletTree(const vector& v, const vector& idxs, T lo, T hi): lo(lo), hi(hi), idxs(idxs), left_sum(1, 0) { if (is_leaf()) { return; } T mid = (lo + hi) / 2; vector lo_idxs, hi_idxs; left_sum.reserve(idxs.size() + 1); for (size_t idx : idxs) { if (v[idx] < mid) { lo_idxs.push_back(idx); left_sum.push_back(left_sum.back() + 1); } else { hi_idxs.push_back(idx); left_sum.push_back(left_sum.back()); } } children[0] = make_unique(v, lo_idxs, lo, mid); children[1] = make_unique(v, hi_idxs, mid, hi); } WaveletTree(const vector& v) : WaveletTree(v, build_idxs(v.size()), *min_element(v.begin(), v.end()), *max_element(v.begin(), v.end()) + 1) {} bool is_leaf() const { return lo + 1 == hi || idxs.empty(); } // Find the index of the k-th smallest value in the range [l, r) // k is 0 indexed. // Returns -1 on failure ssize_t kth(size_t l, size_t r, size_t k) { if (is_leaf()) { return k < idxs.size() ? idxs[k] : -1; } int32_t in_left = left_sum[r] - left_sum[l]; if (k < in_left) { return children[0]->kth(left_sum[l], left_sum[r], k); } else { return children[1]->kth(l - left_sum[l], r - left_sum[r], k - in_left); } } T lo; T hi; vector idxs; // left_sum[i] = # of the first i indexes <= mid vector left_sum; array, 2> children; }; /** * Online solution: * * Build a suffix array on s. Then for each query, do a binary search on the * suffix array to find the range of suffixes which begin with t. This can be * done with a binary search in O(|t| log |s|). After that, the problem has * been transformed into k-th smallest query on a range. This can again be * solved in O(log |s|) using a wavelet tree. * * Overall time complexity: O((|s| + \sum |t|) log |s|) * * By replacing the binary search with hashing and using a linear time suffix * array construction algorithm, I think we can improve this to O(|s| + (\sum * |t|) + q log |s|). Seems like overkill, and is possibly slower. */ // is s[i:] > t (>= if strict) bool check(const string& s, const string& t, size_t idx, bool strict) { for (int i = 0; i < t.size(); ++i) { if (idx + i > s.size() || s[idx + i] < t[i]) { return false; } else if (s[idx + i] > t[i]) { return true; } } return strict; } // Find the first index i where s[i:] > t (or s[i:] >= t if strict). size_t lookup(const SuffixArray& sa, const string& s, const string& t, bool strict) { size_t lo = 0; size_t hi = s.size(); if (!check(s, t, sa.sa[hi], strict)) { return hi + 1; } while (lo + 1 < hi) { size_t mid = (lo + hi) / 2; if (check(s, t, sa.sa[mid], strict)) { hi = mid; } else { lo = mid; } } return hi; } pair lookup(const SuffixArray& sa, const string& s, const string& t) { return make_pair(lookup(sa, s, t, true), lookup(sa, s, t, false)); } int main() { ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); string s; cin >> s; SuffixArray sa(s); WaveletTree wt(sa.sa); int q; cin >> q; while (q-- > 0) { string t; int k; cin >> t >> k; --k; auto res = lookup(sa, s, t); int ans = wt.kth(res.first, res.second, k); // It's possible that the returned index is 0 - the empty string. // explicitly handle this case. if (ans == 0) { ans = -1; } if (ans != -1) { ans = sa.sa[ans] + 1; } cout << ans << '\n'; } return 0; }