#include #include #include #include #include #include #include #include #include using namespace std; /** * Consider the following algorithm: group all queries by their length and answer all of them at once. * For a fixed length, compute a hash of all substrings of that length. This takes O(n) time using a * rolling hash. Then find the k-th occurrence of the needed hash value. * * Let n = |s|. At first glance, this seems O(n^2). There are O(n) different * lengths t can take on, and each length takes O(n) time. However, because * there is a restriction on the length of the sum of t, there are actually * only O(sqrt(\sum |t|)) different values the length of t can take on. To see * this, consider a query string of length 1, then 2, then 3, and so on. The * total length grows quadratically. * * Therefore this algorithm runs in O(n * sqrt(\sum |t|)) time. */ template struct modint { constexpr static int64_t fix(int64_t x) { x %= MOD; if (x < 0) x += MOD; return x; } int64_t v; constexpr modint(const int64_t _v = 0): v(fix(_v)) {} constexpr modint(const modint& other) { v = other.v; } // not generally useful, but good for maps? bool operator<(const modint& other) const { return v < other.v; } bool operator>(const modint& other) const { return v > other.v; } bool operator<=(const modint& other) const { return v <= other.v; } bool operator>=(const modint& other) const { return v >= other.v; } bool operator==(const modint& other) const { return v == other.v; } bool operator!=(const modint& other) const { return v != other.v; } modint& operator+=(const modint& other) { v += other.v; if (v >= MOD) v -= MOD; return *this; } modint& operator-=(const modint& other) { v -= other.v; if (v < 0) v += MOD; return *this; } modint& operator*=(const modint& other) { v = 1LL * v * other.v % MOD; return *this; } modint& operator/=(const modint& other) { *this *= other.inv(); return *this; } modint operator+(const modint& other) const { return modint(v) += other; } modint operator-(const modint& other) const { return modint(v) -= other; } modint operator*(const modint& other) const { return modint(v) *= other; } modint operator/(const modint& other) const { return modint(v) /= other; } static modint pow(const modint& b, int64_t e) { modint res = 1; modint bb = b; for (int64_t p = 1; p <= e; p <<= 1) { if (e & p) { res *= bb; } bb *= bb; } return res; } modint pow(int64_t e) const { return pow(*this, e); } modint inv() const { return pow(MOD - 2); } friend istream& operator>>(istream& is, modint& m) { int64_t x; is >> x; m.v = fix(x); return is; } friend ostream& operator<<(ostream& os, const modint& m) { return os << m.v; } }; using H = modint<1'000'000'007>; namespace std { template <> struct hash { size_t operator()(H v) const { return static_cast(v.v); } }; } // chosen by fair dice roll // guaranteed to be random constexpr static H B(97); vector rolling_hashes(const string& s, int len) { H pow = 1; for (int i = 0; i < len; ++i) { pow *= B; } vector prefixes(s.size() + 1, 0); vector res; res.reserve(s.size()); for (int i = 0; i < s.size(); ++i) { prefixes[i + 1] = prefixes[i] * B + s[i]; if (i + 1 >= len) { res.push_back(prefixes[i + 1] - prefixes[i + 1 - len] * pow); } } return res; } H rk_hash(const string& s) { vector res = rolling_hashes(s, s.size()); assert(res.size() == 1); return res[0]; } int main() { ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); string s; cin >> s; int n = s.size(); int q; cin >> q; using query = tuple; vector> queries(n); for (int i = 0; i < q; ++i) { string t; int k; cin >> t >> k; int len = t.size(); --k; if (len <= queries.size()) { queries[len - 1].emplace_back(rk_hash(t), k, i); } } vector ans(q, -1); // This looks O(n^2) but is actually O(n^1.5) for (int len = 0; len < n; ++len) { if (queries[len].empty()) { continue; } vector hashes = rolling_hashes(s, len + 1); unordered_map> idxs; for (int i = 0; i < hashes.size(); ++i) { idxs[hashes[i]].push_back(i); } for (auto [t, k, idx] : queries[len]) { auto it = idxs.find(t); if (it != idxs.end() && it->second.size() > k) { // 1-indexing ans[idx] = it->second[k] + 1; } } } for (auto x : ans) { cout << x << '\n'; } return 0; }