Kirei and the Linear Function (Codeforces 1729F)
Tags: Math, hashing

Given the string $$$s$$$ of decimal digits (0-9) of length $$$n$$$.A substring is a sequence of consecutive characters of a string. The substring of this string is defined by a pair of indexes — with its left and right ends. So, each pair of indexes ($$$l, r$$$), where $$$1 \le l \le r \le n$$$, corresponds to a substring of the string $$$s$$$. We will define as $$$v(l,r)$$$ the numeric value of the corresponding substring (leading zeros are allowed in it).For example, if $$$n=7$$$, $$$s=$$$"1003004", then $$$v(1,3)=100$$$, $$$v(2,3)=0$$$ and $$$v(2,7)=3004$$$.You are given $$$n$$$, $$$s$$$ and an integer $$$w$$$ ($$$1 \le w < n$$$).You need to process $$$m$$$ queries, each of which is characterized by $$$3$$$ numbers $$$l_i, r_i, k_i$$$ ($$$1 \le l_i \le r_i \le n; 0 \le k_i \le 8$$$).The answer to the $$$i$$$th query is such a pair of substrings of length $$$w$$$ that if we denote them as $$$(L_1, L_1+w-1)$$$ and $$$(L_2, L_2+w-1)$$$, then:  $$$L_1 \ne L_2$$$, that is, the substrings are different;  the remainder of dividing a number $$$v(L_1, L_1+w-1) \cdot v(l_i, r_i) + v(L_2, L_2 + w - 1)$$$ by $$$9$$$ is equal to $$$k_i$$$. If there are many matching substring pairs, then find a pair where $$$L_1$$$ is as small as possible. If there are many matching pairs in this case, then minimize $$$L_2$$$.Note that the answer may not exist.

Input: The first line contains a single integer $$$t$$$ ($$$1 \le t \le 10^4$$$) — number of input test cases.The first line of each test case contains a string $$$s$$$, which contains only the characters 0-9 and has a length $$$n$$$ ($$$2 \le n \le 2 \cdot 10^5$$$).The second line contains two integers $$$w, m$$$ ($$$1 \le w < n, 1 \le m \le 2 \cdot 10^5$$$), where $$$n$$$ — is the length of the given string $$$s$$$. The number $$$w$$$ denotes the lengths of the substrings being searched for, and $$$m$$$ is the number of queries to be processed.The following $$$m$$$ lines contain integers $$$l_i, r_i, k_i$$$ ($$$1 \le l_i \le r_i \le n$$$, $$$0 \le k_i \le 8$$$) — $$$i$$$th query parameters.It is guaranteed that the sum of $$$n$$$ over all test cases does not exceed $$$2 \cdot 10^5$$$. It is also guaranteed that the sum of $$$m$$$ over all test cases does not exceed $$$2 \cdot 10^5$$$.

Output: For each request, print in a separate line:   left borders of the required substrings: $$$L_1$$$ and $$$L_2$$$; -1 -1 otherwise, if there is no solution. If there are several solutions, minimize $$$L_1$$$ first, and minimize $$$L_2$$$ second.

Note: Consider the first test case of example inputs. In this test case $$$n=7$$$, $$$s=$$$"1003004", $$$w=4$$$ and one query $$$l_1=1$$$, $$$r_1=2$$$, $$$k_1=1$$$. Note that $$$v(1,2)=10$$$. We need to find a pair of substrings of length $$$4$$$ such that $$$v(L_1, L_1+3)\cdot10+v(L_2,L_2+3)$$$ has a remainder of $$$k_1=1$$$ when divided by $$$9$$$. The values $$$L_1=2, L_2=4$$$ actually satisfy all the requirements: $$$v(L_1, L_1+w-1)=v(2,5)=30$$$, $$$v(L_2, L_2+w-1)=v(4,7)=3004$$$. Indeed, $$$30\cdot10+3004=3304$$$, which has a remainder of $$$1$$$ when divided by $$$9$$$. It can be shown that $$$L_1=2$$$ is the minimum possible value, and $$$L_2=4$$$ is the minimum possible with $$$L_1=2$$$.

Sample Inputs:
5
1003004
4 1
1 2 1
179572007
4 2
2 7 3
2 7 4
111
2 1
2 2 6
0000
1 2
1 4 0
1 4 1
484
1 5
2 2 0
2 3 7
1 2 5
3 3 8
2 2 6

Sample Outputs:
2 4
1 5
1 2
-1 -1
1 2
-1 -1
1 3
1 3
-1 -1
-1 -1
-1 -1

Solution (Python):
from collections import defaultdict
import sys
input = sys.stdin.readline

def get_remainder(i, j):
    if i == 0:
        return pref[j]
    else:
        return (pref[j] - pref[i - 1]) % 9
for _ in range(int(input())):
    s = [int(i) for i in input().strip()]
    n = len(s)
    pref = []
    cur = 0
    for i in range(n):
        cur = cur * 10 + s[i]
        cur %= 9
        pref.append(cur)
    (w, m) = map(int, input().split())
    dic = defaultdict(lambda : [])
    for i in range(n - w + 1):
        cur = get_remainder(i, i + w - 1)
        dic[cur].append(i)
    ans = []
    for qi in range(m):
        (l, r, k) = map(int, input().split())
        cur = get_remainder(l - 1, r - 1)
        flag = 0
        for i in dic:
            req = (-(cur * i) + k) % 9
            lst = [req]
            if req != i and req in dic:
                ans.append([dic[i][0] + 1, dic[req][0] + 1])
                flag = 1
            elif req == i and len(dic[i]) >= 2:
                ans.append([dic[i][0] + 1, dic[i][1] + 1])
                flag = 1
            if flag:
                break
        if not flag:
            ans.append([-1, -1])
    for i in ans:
        print(*i)

Solution (Python):
import sys, os
import math
if os.path.exists('in.txt'):
    sys.stdin = open('in.txt', 'r')
from io import BytesIO, IOBase
from heapq import *
from bisect import *
from collections import *
from itertools import *

class SegTree:

    def __init__(self, arr, n):
        self.n = n
        self.size = 1
        while self.size <= n:
            self.size *= 2
        self.ans = [0] * (2 * self.size)
        self.build(arr, n)

    def builtUtil(self, arr, sl, sr, idx):
        if sl == sr:
            self.ans[idx] = arr[sl]
            return
        mid = (sl + sr) // 2
        self.builtUtil(arr, sl, mid, 2 * idx + 1)
        self.builtUtil(arr, mid + 1, sr, 2 * idx + 2)
        self.ans[idx] = self.ans[2 * idx + 1] + self.ans[2 * idx + 2]

    def build(self, arr, n):
        self.builtUtil(arr, 0, n - 1, 0)

    def sumUtil(self, sl, sr, l, r, idx):
        if r < sl or l > sr:
            return 0
        if l <= sl and sr <= r:
            return self.ans[idx]
        mid = (sl + sr) // 2
        return self.sumUtil(sl, mid, l, r, 2 * idx + 1) + self.sumUtil(mid + 1, sr, l, r, 2 * idx + 2)

    def sum(self, l, r):
        return self.sumUtil(0, self.n - 1, l, r, 0)

    def upUtil(self, sl, sr, idx, idx1, val):
        if idx1 > sr or idx1 < sl:
            return
        if sl == sr:
            self.ans[idx] = val
            return
        mid = (sl + sr) // 2
        self.upUtil(sl, mid, 2 * idx + 1, idx1, val)
        self.upUtil(mid + 1, sr, 2 * idx + 2, idx1, val)
        self.ans[idx] = self.ans[2 * idx + 1] + self.ans[2 * idx + 2]

    def up(self, idx, val):
        self.upUtil(0, self.n - 1, 0, idx, val)

class SortedList:

    def __init__(self, iterable=[], _load=200):
        """"""
        values = sorted(iterable)
        self._len = _len = len(values)
        self._load = _load
        self._lists = _lists = [values[i:i + _load] for i in range(0, _len, _load)]
        self._list_lens = [len(_list) for _list in _lists]
        self._mins = [_list[0] for _list in _lists]
        self._fen_tree = []
        self._rebuild = True

    def _fen_build(self):
        """"""
        self._fen_tree[:] = self._list_lens
        _fen_tree = self._fen_tree
        for i in range(len(_fen_tree)):
            if i | i + 1 < len(_fen_tree):
                _fen_tree[i | i + 1] += _fen_tree[i]
        self._rebuild = False

    def _fen_update(self, index, value):
        """"""
        if not self._rebuild:
            _fen_tree = self._fen_tree
            while index < len(_fen_tree):
                _fen_tree[index] += value
                index |= index + 1

    def _fen_query(self, end):
        """"""
        if self._rebuild:
            self._fen_build()
        _fen_tree = self._fen_tree
        x = 0
        while end:
            x += _fen_tree[end - 1]
            end &= end - 1
        return x

    def _fen_findkth(self, k):
        """"""
        _list_lens = self._list_lens
        if k < _list_lens[0]:
            return (0, k)
        if k >= self._len - _list_lens[-1]:
            return (len(_list_lens) - 1, k + _list_lens[-1] - self._len)
        if self._rebuild:
            self._fen_build()
        _fen_tree = self._fen_tree
        idx = -1
        for d in reversed(range(len(_fen_tree).bit_length())):
            right_idx = idx + (1 << d)
            if right_idx < len(_fen_tree) and k >= _fen_tree[right_idx]:
                idx = right_idx
                k -= _fen_tree[idx]
        return (idx + 1, k)

    def _delete(self, pos, idx):
        """"""
        _lists = self._lists
        _mins = self._mins
        _list_lens = self._list_lens
        self._len -= 1
        self._fen_update(pos, -1)
        del _lists[pos][idx]
        _list_lens[pos] -= 1
        if _list_lens[pos]:
            _mins[pos] = _lists[pos][0]
        else:
            del _lists[pos]
            del _list_lens[pos]
            del _mins[pos]
            self._rebuild = True

    def _loc_left(self, value):
        """"""
        if not self._len:
            return (0, 0)
        _lists = self._lists
        _mins = self._mins
        (lo, pos) = (-1, len(_lists) - 1)
        while lo + 1 < pos:
            mi = lo + pos >> 1
            if value <= _mins[mi]:
                pos = mi
            else:
                lo = mi
        if pos and value <= _lists[pos - 1][-1]:
            pos -= 1
        _list = _lists[pos]
        (lo, idx) = (-1, len(_list))
        while lo + 1 < idx:
            mi = lo + idx >> 1
            if value <= _list[mi]:
                idx = mi
            else:
                lo = mi
        return (pos, idx)

    def _loc_right(self, value):
        """"""
        if not self._len:
            return (0, 0)
        _lists = self._lists
        _mins = self._mins
        (pos, hi) = (0, len(_lists))
        while pos + 1 < hi:
            mi = pos + hi >> 1
            if value < _mins[mi]:
                hi = mi
            else:
                pos = mi
        _list = _lists[pos]
        (lo, idx) = (-1, len(_list))
        while lo + 1 < idx:
            mi = lo + idx >> 1
            if value < _list[mi]:
                idx = mi
            else:
                lo = mi
        return (pos, idx)

    def add(self, value):
        """"""
        _load = self._load
        _lists = self._lists
        _mins = self._mins
        _list_lens = self._list_lens
        self._len += 1
        if _lists:
            (pos, idx) = self._loc_right(value)
            self._fen_update(pos, 1)
            _list = _lists[pos]
            _list.insert(idx, value)
            _list_lens[pos] += 1
            _mins[pos] = _list[0]
            if _load + _load < len(_list):
                _lists.insert(pos + 1, _list[_load:])
                _list_lens.insert(pos + 1, len(_list) - _load)
                _mins.insert(pos + 1, _list[_load])
                _list_lens[pos] = _load
                del _list[_load:]
                self._rebuild = True
        else:
            _lists.append([value])
            _mins.append(value)
            _list_lens.append(1)
            self._rebuild = True

    def discard(self, value):
        """"""
        _lists = self._lists
        if _lists:
            (pos, idx) = self._loc_right(value)
            if idx and _lists[pos][idx - 1] == value:
                self._delete(pos, idx - 1)

    def remove(self, value):
        """"""
        _len = self._len
        self.discard(value)
        if _len == self._len:
            raise ValueError('{0!r} not in list'.format(value))

    def pop(self, index=-1):
        """"""
        (pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
        value = self._lists[pos][idx]
        self._delete(pos, idx)
        return value

    def bisect_left(self, value):
        """"""
        (pos, idx) = self._loc_left(value)
        return self._fen_query(pos) + idx

    def bisect_right(self, value):
        """"""
        (pos, idx) = self._loc_right(value)
        return self._fen_query(pos) + idx

    def count(self, value):
        """"""
        return self.bisect_right(value) - self.bisect_left(value)

    def __len__(self):
        """"""
        return self._len

    def __getitem__(self, index):
        """"""
        (pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
        return self._lists[pos][idx]

    def __delitem__(self, index):
        """"""
        (pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
        self._delete(pos, idx)

    def __contains__(self, value):
        """"""
        _lists = self._lists
        if _lists:
            (pos, idx) = self._loc_left(value)
            return idx < len(_lists[pos]) and _lists[pos][idx] == value
        return False

    def __iter__(self):
        """"""
        return (value for _list in self._lists for value in _list)

    def __reversed__(self):
        """"""
        return (value for _list in reversed(self._lists) for value in reversed(_list))

    def __repr__(self):
        """"""
        return 'SortedList({0})'.format(list(self))

def prime_factors(n):
    (c, t) = ([], n)
    for j in range(2, int(n ** 0.5) + 1):
        if t % j == 0:
            c.append(j)
            while t % j == 0:
                t //= j
    if t > 1:
        c.append(t)
    return c

def all_factors(n):
    i = 1
    ans = []
    while i < int(n ** 0.5) + 1:
        if n % i == 0:
            ans.append(i)
            if n // i != i:
                ans.append(n // i)
        i += 1
    return ans

def sum_all_fac(n):
    ans = 1
    p = 2
    while p < int(n ** 0.5) + 1:
        cnt = 0
        while n % p == 0:
            n //= p
            cnt += 1
        ans *= (p ** cnt - 1) // (p - 1)
        p += 1
    return ans

def number_of_divisor(n):
    ans = 1
    p = 2
    while p < int(n ** 0.5) + 1:
        cnt = 0
        while n % p == 0:
            n //= p
            cnt += 1
        ans *= cnt + 1
        p += 1
    if n > 1:
        ans *= 2
    return ans

def sieve(n):
    num = [i for i in range(n + 1)]
    pr = [True] * (n + 1)
    p = 2
    while p < int(n ** 0.5) + 1:
        if pr[p]:
            for i in range(p * p, n + 1, p):
                pr[i] = False
                num[i] = p
        p += 1
    return num

def ans_fl(fl):
    if fl:
        return 'YES'
    else:
        return 'NO'

def get_factor_logn(num, n):
    d = {}
    while num[n] != 1:
        d[num[n]] = d.get(num[n], 0) + 1
        n //= num[n]
    return d

def main():
    for _ in range(mi()):
        s = list(map(int, list(input())))
        pref = list(accumulate([0] + s))
        n = len(s)
        (w, m) = MI()
        idx = defaultdict(list)
        for i in range(w, n + 1):
            x = (pref[i] - pref[i - w]) % 9
            idx[x].append(i - w + 1)
        for __ in range(m):
            (a, b, c) = MI()
            val = (pref[b] - pref[a - 1]) % 9
            ans = [10 ** 10, 10 ** 10]
            for i in range(9):
                if i not in idx:
                    continue
                z = (c - val * i) % 9
                if z not in idx:
                    continue
                lb = idx[i][0]
                if i != z:
                    rb = idx[z][0]
                else:
                    if len(idx[i]) == 1:
                        continue
                    rb = idx[z][1]
                if ans[0] > lb:
                    ans = [lb, rb]
                elif ans[0] == lb:
                    if ans[1] > rb:
                        ans = [lb, rb]
            if ans == [10 ** 10, 10 ** 10]:
                print(-1, -1)
            else:
                print(ans[0], ans[1])
BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = 'x' in file.mode or 'r' not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            (self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b'\n') + (not b)
            ptr = self.buffer.tell()
            (self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            (self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode('ascii'))
        self.read = lambda : self.buffer.read().decode('ascii')
        self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def mi():
    return int(input())

def MI():
    return map(int, input().split())

def LMI():
    return list(map(int, input().split()))

def outP(var):
    sys.stdout.write(str(var) + '\n')
MOD = 10 ** 9 + 7
mod = 998244353
dir4 = [(1, 0), (0, 1), (-1, 0), (0, -1)]
dir8 = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)]
if __name__ == '__main__':
    main()

Solution (Python):
import sys
input = sys.stdin.readline
print = sys.stdout.write

def sol():
    s = list(map(int, input().strip()))
    idx_f = [0 for _ in range(9)]
    idx_s = [0 for _ in range(9)]
    (w, m) = map(int, input().split())
    n = len(s)
    cur = 0
    for i in range(w):
        cur += s[i]
    idx_f[cur % 9] = 1
    for i in range(w, n):
        cur += s[i] - s[i - w]
        mod = cur % 9
        if idx_f[mod]:
            idx_s[mod] = i - w + 2 if not idx_s[mod] else idx_s[mod]
        else:
            idx_f[mod] = i - w + 2
    pref = [0 for _ in range(n)]
    pref[0] = s[0]
    for i in range(1, n):
        pref[i] = (pref[i - 1] * 10 + s[i]) % 9
    for i in range(m):
        (l, r, k) = map(int, input().split())
        l -= 1
        r -= 1
        num = pref[r] if l == 0 else (pref[r] - pref[l - 1] + 9) % 9
        ans = [10 ** 9, 10 ** 9]
        for j in range(9):
            mod = (k + 9 - num * j % 9) % 9
            if mod == j:
                if idx_f[mod] != 0 and idx_s[mod] != 0:
                    cur = [ans, [idx_f[mod], idx_s[mod]]]
                    cur.sort()
                    ans = cur[0]
            elif idx_f[j] and idx_f[mod]:
                cur = [ans, [idx_f[j], idx_f[mod]]]
                cur.sort()
                ans = cur[0]
        if ans[0] == 10 ** 9:
            print('-1 -1\n')
        else:
            print(str(ans[0]) + ' ' + str(ans[1]) + '\n')
    return
tn = int(input())
for i in range(tn):
    sol()