import sys,random,bisect from collections import deque,defaultdict from heapq import heapify,heappop,heappush from itertools import permutations from math import gcd,log from math import sqrt, ceil from bisect import bisect_left, bisect_right from typing import Iterable input = lambda :sys.stdin.readline().rstrip() mi = lambda :map(int,input().split()) li = lambda :list(mi()) import copy import functools import typing def _sa_naive(s: typing.List[int]) -> typing.List[int]: sa = list(range(len(s))) return sorted(sa, key=lambda i: s[i:]) def _sa_doubling(s: typing.List[int]) -> typing.List[int]: n = len(s) sa = list(range(n)) rnk = copy.deepcopy(s) tmp = [0] * n k = 1 while k < n: def cmp(x: int, y: int) -> bool: if rnk[x] != rnk[y]: return rnk[x] - rnk[y] rx = rnk[x + k] if x + k < n else -1 ry = rnk[y + k] if y + k < n else -1 return rx - ry sa.sort(key=functools.cmp_to_key(cmp)) tmp[sa[0]] = 0 for i in range(1, n): tmp[sa[i]] = tmp[sa[i - 1]] + (1 if cmp(sa[i - 1], sa[i]) else 0) tmp, rnk = rnk, tmp k *= 2 return sa def _sa_is(s: typing.List[int], upper: int) -> typing.List[int]: ''' SA-IS, linear-time suffix array construction Reference: G. Nong, S. Zhang, and W. H. Chan, Two Efficient Algorithms for Linear Time Suffix Array Construction ''' threshold_naive = 10 threshold_doubling = 40 n = len(s) if n == 0: return [] if n == 1: return [0] if n == 2: if s[0] < s[1]: return [0, 1] else: return [1, 0] if n < threshold_naive: return _sa_naive(s) if n < threshold_doubling: return _sa_doubling(s) sa = [0] * n ls = [False] * n for i in range(n - 2, -1, -1): if s[i] == s[i + 1]: ls[i] = ls[i + 1] else: ls[i] = s[i] < s[i + 1] sum_l = [0] * (upper + 1) sum_s = [0] * (upper + 1) for i in range(n): if not ls[i]: sum_s[s[i]] += 1 else: sum_l[s[i] + 1] += 1 for i in range(upper + 1): sum_s[i] += sum_l[i] if i < upper: sum_l[i + 1] += sum_s[i] def induce(lms: typing.List[int]) -> None: nonlocal sa sa = [-1] * n buf = copy.deepcopy(sum_s) for d in lms: if d == n: continue sa[buf[s[d]]] = d buf[s[d]] += 1 buf = copy.deepcopy(sum_l) sa[buf[s[n - 1]]] = n - 1 buf[s[n - 1]] += 1 for i in range(n): v = sa[i] if v >= 1 and not ls[v - 1]: sa[buf[s[v - 1]]] = v - 1 buf[s[v - 1]] += 1 buf = copy.deepcopy(sum_l) for i in range(n - 1, -1, -1): v = sa[i] if v >= 1 and ls[v - 1]: buf[s[v - 1] + 1] -= 1 sa[buf[s[v - 1] + 1]] = v - 1 lms_map = [-1] * (n + 1) m = 0 for i in range(1, n): if not ls[i - 1] and ls[i]: lms_map[i] = m m += 1 lms = [] for i in range(1, n): if not ls[i - 1] and ls[i]: lms.append(i) induce(lms) if m: sorted_lms = [] for v in sa: if lms_map[v] != -1: sorted_lms.append(v) rec_s = [0] * m rec_upper = 0 rec_s[lms_map[sorted_lms[0]]] = 0 for i in range(1, m): left = sorted_lms[i - 1] right = sorted_lms[i] if lms_map[left] + 1 < m: end_l = lms[lms_map[left] + 1] else: end_l = n if lms_map[right] + 1 < m: end_r = lms[lms_map[right] + 1] else: end_r = n same = True if end_l - left != end_r - right: same = False else: while left < end_l: if s[left] != s[right]: break left += 1 right += 1 if left == n or s[left] != s[right]: same = False if not same: rec_upper += 1 rec_s[lms_map[sorted_lms[i]]] = rec_upper rec_sa = _sa_is(rec_s, rec_upper) for i in range(m): sorted_lms[i] = lms[rec_sa[i]] induce(sorted_lms) return sa def suffix_array(s: typing.Union[str, typing.List[int]], upper: typing.Optional[int] = None) -> typing.List[int]: if isinstance(s, str): return _sa_is([ord(c) for c in s], 255) elif upper is None: n = len(s) idx = list(range(n)) idx.sort(key=functools.cmp_to_key(lambda l, r: s[l] - s[r])) s2 = [0] * n now = 0 for i in range(n): if i and s[idx[i - 1]] != s[idx[i]]: now += 1 s2[idx[i]] = now return _sa_is(s2, now) else: assert 0 <= upper for d in s: assert 0 <= d <= upper return _sa_is(s, upper) def lcp_array(s: typing.Union[str, typing.List[int]], sa: typing.List[int]) -> typing.List[int]: ''' Reference: T. Kasai, G. Lee, H. Arimura, S. Arikawa, and K. Park, Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its Applications ''' if isinstance(s, str): s = [ord(c) for c in s] n = len(s) assert n >= 1 rnk = [0] * n for i in range(n): rnk[sa[i]] = i lcp = [0] * (n - 1) h = 0 for i in range(n): if h > 0: h -= 1 if rnk[i] == 0: continue j = sa[rnk[i] - 1] while j + h < n and i + h < n: if s[j + h] != s[i + h]: break h += 1 lcp[rnk[i] - 1] = h return lcp def z_algorithm(s: typing.Union[str, typing.List[int]]) -> typing.List[int]: ''' Reference: D. Gusfield, Algorithms on Strings, Trees, and Sequences: Computer Science and Computational Biology ''' if isinstance(s, str): s = [ord(c) for c in s] n = len(s) if n == 0: return [] z = [0] * n j = 0 for i in range(1, n): z[i] = 0 if j + z[j] <= i else min(j + z[j] - i, z[i - j]) while i + z[i] < n and s[z[i]] == s[i + z[i]]: z[i] += 1 if j + z[j] < i + z[i]: j = i z[0] = n return z class SegmentTree: def __init__(self, init_val, segfunc, ide_ele): n = len(init_val) self.segfunc = segfunc self.ide_ele = ide_ele self.num = 1 << (n - 1).bit_length() self.tree = [ide_ele] * 2 * self.num self.size = n for i in range(n): self.tree[self.num + i] = init_val[i] for i in range(self.num - 1, 0, -1): self.tree[i] = self.segfunc(self.tree[2 * i], self.tree[2 * i + 1]) def update(self, k, x): k += self.num self.tree[k] = x while k > 1: k >>= 1 self.tree[k] = self.segfunc(self.tree[2*k], self.tree[2*k+1]) def query(self, l, r): if r==self.size: r = self.num res = self.ide_ele l += self.num r += self.num right = [] while l < r: if l & 1: res = self.segfunc(res, self.tree[l]) l += 1 if r & 1: right.append(self.tree[r-1]) l >>= 1 r >>= 1 for e in right[::-1]: res = self.segfunc(res,e) return res class SortedSet: @staticmethod def _new_bucket_size(size: int) -> int: return int(ceil(sqrt(size))) def _build(self, a: list) -> None: size = self.size = len(a) bucket_size = self._new_bucket_size(self.size) self.a = [a[size * i // bucket_size: size * (i + 1) // bucket_size] for i in range(bucket_size)] def __init__(self, a: Iterable = []): "Make a new SortedSet from iterable." self._build(sorted(set(a))) def __iter__(self): for i in self.a: for j in i: yield j def __len__(self): return self.size def __repr__(self) -> str: return str(self.a) def __str__(self) -> str: s = str(list(self)) return "{" + s[1 : len(s) - 1] + "}" def _bucket_index(self, x) -> int: "Find the index of the bucket which should contain x." ok = -1 ng = len(self.a) a = self.a while ng - ok > 1: mid = (ng + ok) >> 1 if a[mid][0] <= x: ok = mid else: ng = mid if ok == -1: return 0 if ng == len(self.a): return ok if a[ok][-1] < x: return ok + (len(a[ok]) > len(a[ok + 1])) return ok def add(self, x) -> bool: "Add an element and return True if added." if self.size == 0: self._build([x]) return True a = self.a[self._bucket_index(x)] i = bisect_left(a, x) if i != len(a) and a[i] == x: return False a.insert(i, x) self.size += 1 if len(a) > len(self.a) * 4: self._build(list(self)) return True def lt(self, x): "Return the largest element < x, or None if it doesn't exist." if self.size == 0: return None i = self._bucket_index(x) a = self.a if a[i][0] >= x: return a[i - 1][-1] if i else None return a[i][bisect_left(a[i], x) - 1] def gt(self, x): "Return the smallest element > x, or None if it doesn't exist." if self.size == 0: return None i = self._bucket_index(x) a = self.a if a[i][-1] <= x: return a[i + 1][0] if i + 1 < len(self.a) else None return a[i][bisect_right(a[i], x)] class SparseTable(): def __init__(self,A,merge_func,ide_ele): N = len(A) self.merge_func = merge_func self.lg = [0]*(N + 1) for i in range(2, N+1): self.lg[i] = self.lg[i >> 1] + 1 self.pow_2 = [pow(2,i) for i in range(20)] self.table = [None]*(self.lg[N] + 1) st0 = self.table[0] = [a for a in A] b = 1 for i in range(self.lg[N]): st0 = self.table[i+1] = [self.merge_func(u,v) for u, v in zip(st0, st0[b:])] b <<= 1 def query(self,s,t): t -= 1 b = t-s+1 m = self.lg[b] return self.merge_func(self.table[m][s],self.table[m][t-self.pow_2[m]+1]) for test in range(int(input())): S,T,Q = input().split() Q = int(Q) ST = S + "?" + T sa = suffix_array(ST) suffix_to_idx = [-1] * (len(sa)) for i in range(len(sa)): suffix_to_idx[sa[i]] = i lcp = [-1] + lcp_array(ST,sa) seg = SparseTable(lcp,min,10**9) query = [] ok = [0] * Q ng = [-1] * Q for i in range(Q): a,b = mi() query.append((a,b)) ng[i] = min(a,b)+1 for _ in range(17): tmp_query = [[] for a in range(len(S))] for i in range(Q): a,b = query[i] mid = (ok[i]+ng[i])//2 if ng[i]==ok[i]+1: continue tmp_query[a-mid].append((b,i)) a_set = SortedSet() for a in range(len(S)): a_set.add(suffix_to_idx[a]) for b,i in tmp_query[a]: b = len(S) + 1 + len(T) - b b = suffix_to_idx[b] mid = (ok[i]+ng[i])//2 L = a_set.lt(b) flag = False if L: if seg.query(L+1,b+1) >= mid: flag = True R = a_set.gt(b) if R: if seg.query(b+1,R+1) >= mid: flag = True if flag: ok[i] = mid else: ng[i] = mid print("Case #{}:".format(test+1),*ok)