#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 1010005;
const char SENTI = 'a' - 1;

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N >> 1], offr[N >> 1], offpl[N >> 1], offpr[N >> 1];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix; {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	}
	int suffix; {
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) < k) {
				R = M;
			} else {
				L = M + 1;
			}
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N >> 1];
int ans[N >> 1], ansmx[N >> 1];
int tree[N], leaf[N];

void prepare() {
	++g;
	for (int i = 0; i < g; ++i)
		tree[i + g] = i;
	for (int i = 2 * (g+1) - 1; i > 0; --i)
		tree[i >> 1] = min(tree[i], tree[i ^ 1]);
}

void add(int i, int x) {
	for (leaf[i] += x, i += g; i > 0; i >>= 1) {
		int l1 = leaf[tree[i]];
		int l2 = leaf[tree[i ^ 1]];
		tree[i >> 1] = (l1 > l2 || (l1 == l2 && tree[i] < tree[i ^ 1]) ? tree[i] : tree[i ^ 1]);
	}
}

int queryTree(int a, int b) {
	int ans = tree[a + g];
	for (a += g, b += g + 1; a < b; a >>= 1, b >>= 1) {
		if (a & 1) {
			// cout << "Found: " << a << ' ' << tree[a] << ' ' << leaf[tree[a]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[a]] || (leaf[ans] == leaf[tree[a]] && tree[a] < ans))
				ans = tree[a];
			++a;
		}
		if (b & 1) {
			--b;
			// cout << "Found: " << b << ' ' << tree[b] << ' ' << leaf[tree[b]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[b]] || (leaf[ans] == leaf[tree[b]] && tree[b] < ans))
				ans = tree[b];
		}
		// cout << "at tree: " << a << ' ' << b << ' ' << ans << endl;
	}
	return ans;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	prepare();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 1500005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

void prepare() {
	++g;
	for (int i = 0; i < g; ++i)
		tree[i + g] = i;
	for (int i = 2 * (g+1) - 1; i > 0; --i)
		tree[i >> 1] = min(tree[i], tree[i ^ 1]);
}

void add(int i, int x) {
	for (leaf[i] += x, i += g; i > 0; i >>= 1) {
		int l1 = leaf[tree[i]];
		int l2 = leaf[tree[i ^ 1]];
		tree[i >> 1] = (l1 > l2 || (l1 == l2 && tree[i] < tree[i ^ 1]) ? tree[i] : tree[i ^ 1]);
	}
}

int queryTree(int a, int b) {
	int ans = tree[a + g];
	for (a += g, b += g + 1; a < b; a >>= 1, b >>= 1) {
		if (a & 1) {
			// cout << "Found: " << a << ' ' << tree[a] << ' ' << leaf[tree[a]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[a]] || (leaf[ans] == leaf[tree[a]] && tree[a] < ans))
				ans = tree[a];
			++a;
		}
		if (b & 1) {
			--b;
			// cout << "Found: " << b << ' ' << tree[b] << ' ' << leaf[tree[b]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[b]] || (leaf[ans] == leaf[tree[b]] && tree[b] < ans))
				ans = tree[b];
		}
		// cout << "at tree: " << a << ' ' << b << ' ' << ans << endl;
	}
	return ans;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	prepare();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1500005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L;
	}
	assert(prefix == suffix || querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 0, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P | 1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 0, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 0, int R = g) {
	if (b < L || R < a) return -1;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	if (x == -1) return y;
	if (y == -1) return x;
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1200005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L;
	}
	assert(prefix == suffix || querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 0, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P|1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 0, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 0, int R = g) {
	if (b < L || R < a) return a;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1200005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L;
	}
	assert(prefix == suffix || querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 1, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P|1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 1, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 1, int R = g) {
	if (b < L || R < a) return a;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	if (g) add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	if (g) add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 1500005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 0, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P | 1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 0, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 0, int R = g) {
	if (b < L || R < a) return -1;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	if (x == -1) return y;
	if (y == -1) return x;
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1200005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L;
	}
	assert(prefix == suffix || querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 1, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P|1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 1, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 1, int R = g) {
	if (b < L || R < a) return a;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	if (g) add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	if (g) add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < q; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 1010005;
const char SENTI = 'a' - 1;

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N >> 1], offr[N >> 1], offpl[N >> 1], offpr[N >> 1];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix; {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	}
	int suffix; {
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) < k) {
				R = M;
			} else {
				L = M + 1;
			}
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N >> 1];
int ans[N >> 1], ansmx[N >> 1];
int tree[N], leaf[N];

void prepare() {
	++g;
	for (int i = 0; i < g; ++i)
		tree[i + g] = i;
	for (int i = 2 * (g+1) - 1; i > 0; --i)
		tree[i >> 1] = min(tree[i], tree[i ^ 1]);
}

void add(int i, int x) {
	for (leaf[i] += x, i += g; i > 0; i >>= 1) {
		int l1 = leaf[tree[i]];
		int l2 = leaf[tree[i ^ 1]];
		tree[i >> 1] = (l1 > l2 || (l1 == l2 && tree[i] < tree[i ^ 1]) ? tree[i] : tree[i ^ 1]);
	}
}

int queryTree(int a, int b) {
	int ans = tree[a + g];
	for (a += g, b += g + 1; a < b; a >>= 1, b >>= 1) {
		if (a & 1) {
			// cout << "Found: " << a << ' ' << tree[a] << ' ' << leaf[tree[a]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[a]] || (leaf[ans] == leaf[tree[a]] && tree[a] < ans))
				ans = tree[a];
			++a;
		}
		if (b & 1) {
			--b;
			// cout << "Found: " << b << ' ' << tree[b] << ' ' << leaf[tree[b]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[b]] || (leaf[ans] == leaf[tree[b]] && tree[b] < ans))
				ans = tree[b];
		}
		// cout << "at tree: " << a << ' ' << b << ' ' << ans << endl;
	}
	return ans;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	prepare();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 1010005;
const char SENTI = 'a' - 1;

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N >> 1], offr[N >> 1], offpl[N >> 1], offpr[N >> 1];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix; {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	}
	int suffix; {
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) < k) {
				R = M;
			} else {
				L = M + 1;
			}
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N >> 1];
int ans[N >> 1], ansmx[N >> 1];
int tree[N], leaf[N];

void prepare() {
	++g;
	for (int i = 0; i < g; ++i)
		tree[i + g] = i;
	for (int i = 2 * (g+1) - 1; i > 0; --i)
		tree[i >> 1] = min(tree[i], tree[i ^ 1]);
}

void add(int i, int x) {
	for (leaf[i] += x, i += g; i > 0; i >>= 1) {
		int l1 = leaf[tree[i]];
		int l2 = leaf[tree[i ^ 1]];
		tree[i >> 1] = (l1 > l2 || (l1 == l2 && tree[i] < tree[i ^ 1]) ? tree[i] : tree[i ^ 1]);
	}
}

int queryTree(int a, int b) {
	int ans = tree[a + g];
	for (a += g, b += g + 1; a < b; a >>= 1, b >>= 1) {
		if (a & 1) {
			// cout << "Found: " << a << ' ' << tree[a] << ' ' << leaf[tree[a]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[a]] || (leaf[ans] == leaf[tree[a]] && tree[a] < ans))
				ans = tree[a];
			++a;
		}
		if (b & 1) {
			--b;
			// cout << "Found: " << b << ' ' << tree[b] << ' ' << leaf[tree[b]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[b]] || (leaf[ans] == leaf[tree[b]] && tree[b] < ans))
				ans = tree[b];
		}
		// cout << "at tree: " << a << ' ' << b << ' ' << ans << endl;
	}
	return ans;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	prepare();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1200005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L;
	}
	assert(prefix == suffix || querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 1, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P | 1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 1, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 1, int R = g) {
	if (b < L || R < a) return tree[a];
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1500005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L + 1;
	}
	assert(querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 0, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P | 1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 0, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 0, int R = g) {
	if (b < L || R < a) return -1;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	if (x == -1) return y;
	if (y == -1) return x;
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 2000005;
const char SENTI = 'a' - 1;

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N >> 1], offr[N >> 1], offpl[N >> 1], offpr[N >> 1];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix; {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	}
	int suffix; {
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) < k) {
				R = M;
			} else {
				L = M + 1;
			}
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N >> 1];
int ans[N >> 1], ansmx[N >> 1];
int tree[N], leaf[N];

void prepare() {
	++g;
	for (int i = 0; i < g; ++i)
		tree[i + g] = i;
	for (int i = 2 * (g+1) - 1; i > 0; --i)
		tree[i >> 1] = min(tree[i], tree[i ^ 1]);
}

void add(int i, int x) {
	for (leaf[i] += x, i += g; i > 0; i >>= 1) {
		int l1 = leaf[tree[i]];
		int l2 = leaf[tree[i ^ 1]];
		tree[i >> 1] = (l1 > l2 || (l1 == l2 && tree[i] < tree[i ^ 1]) ? tree[i] : tree[i ^ 1]);
	}
}

int queryTree(int a, int b) {
	int ans = tree[a + g];
	for (a += g, b += g + 1; a < b; a >>= 1, b >>= 1) {
		if (a & 1) {
			// cout << "Found: " << a << ' ' << tree[a] << ' ' << leaf[tree[a]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[a]] || (leaf[ans] == leaf[tree[a]] && tree[a] < ans))
				ans = tree[a];
			++a;
		}
		if (b & 1) {
			--b;
			// cout << "Found: " << b << ' ' << tree[b] << ' ' << leaf[tree[b]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[b]] || (leaf[ans] == leaf[tree[b]] && tree[b] < ans))
				ans = tree[b];
		}
		// cout << "at tree: " << a << ' ' << b << ' ' << ans << endl;
	}
	return ans;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	prepare();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;
const int N = 1200005;
const char SENTI = '$';

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], va[N], lcp[N], gap, n, g, q;
inline bool cmp(int i, int j) {return s[i] != s[j] ? s[i] < s[j] : j < i;}
inline bool equal(int i, int j) {return pos[i] == pos[j] && i + gap < n && pos[i + (gap >> 1)] == pos[j + (gap >> 1)];}
void buildSA(char s[]) {
	for (int i = 0; i < n; ++i)
		sa[i] = i, pos[i] = s[i];
	sort(sa, sa + n, cmp);
	for (gap = 1; gap < n * 2; gap <<= 1) {
		va[sa[0]] = 0;
		for (int i = 1; i < n; ++i) {
			int prev = sa[i - 1], next = sa[i];
			va[next] = equal(prev, next) ? va[prev] : i;
		}
		for (int i = 0; i < n; ++i) {
			pos[i] = va[i];
			va[i] = sa[i];
			c[i] = i;
		}
		for (int i = 0; i < n; ++i) {
			int id = va[i] - gap;
			if (id >= 0)
				sa[c[pos[id]]++] = id;
		}
	}
	for (int i = 0; i < n; ++i)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i <= n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N], offr[N], offpl[N], offpr[N];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix;
	if (lcp[mid - 1] >= k) {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	} else {
		prefix = mid;
	}
	int suffix;
	{
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M) >= k)
				L = M + 1;
			else
				R = M;
		}
		suffix = L;
	}
	assert(prefix == suffix || querySparse(prefix, suffix - 1) >= k);
	return make_pair(prefix, suffix);
}

int Q[N];
int ans[N], ansmx[N];
int tree[N], leaf[N];

#define P (p << 1)
#define M ((L + R) >> 1)

inline void pull(int p) {
	tree[p] = ((leaf[tree[P]] > leaf[tree[P|1]] || (leaf[tree[P]] == leaf[tree[P|1]] && tree[P] < tree[P|1]))
		? tree[P]
		: tree[P|1]);
}

void build(int p = 1, int L = 1, int R = g) {
	if (L == R) {
		tree[p] = L;
		return;
	}
	build(P, L, M);
	build(P|1, M + 1, R);
	pull(p);
}

void add(int i, int x, int p = 1, int L = 1, int R = g) {
	if (L == R) {
		leaf[L] += x;
		return;
	}
	if (i <= M) add(i, x, P, L, M);
	else add(i, x, P|1, M+1, R);
	pull(p);
}

int queryTree(int a, int b, int p = 1, int L = 1, int R = g) {
	if (b < L || R < a) return a;
	if (a <= L && R <= b) return tree[p];
	int x = queryTree(a, b, P, L, M);
	int y = queryTree(a, b, P|1, M+1, R);
	pull(p);
	return (leaf[x] > leaf[y] || (leaf[x] == leaf[y] && x < y)) ? x : y;
}

int insert(int id) {
	int g = grp[sa[id]];
	if (g) add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	if (g) add(g, -1);
}

void mo() {
	build();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		ans[id] = queryTree(l, r);
		if (ans[id] == 0) ans[id] = l;
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int N = 1010005;
const char SENTI = 'a' - 1;

char s[N];
int grp[N], sn[N], sa[N], pos[N], c[N], lcp[N];

inline bool leq(int a, int b, int c, int d) {return a == c ? b <= d : a < c;}
inline bool leq(int a, int b, int c, int d, int e, int f) {return a == d ? leq(b, c, e, f) : a < d;}
inline void radixSort(int a[], int b[], int r[], int n, int K) {
	memset(c, 0, sizeof(int) * (K + 1));
	for (int i = 0; i < n; i++) c[r[a[i]]]++;
	for (int i = 0, sum = 0; i <= K; i++) {
		int t = c[i];
		c[i] = sum;
		sum += t;
	}
	for (int i = 0; i < n; i++) b[c[r[a[i]]]++] = a[i];
}
void DC3algorithm(int s[], int sa[], int n, int K) {
	// sizes
	int nL = (n + 2) / 3;
	int nM = (n + 1) / 3;
	int nR = n / 3;
	int nLR = nL + nR;

	// dummy subarrays
	// int sL[nL], saL[nL], sMR[nLR+3], saMR[nLR+3];

	int *sL = new int[nL], *saL = new int[nL];
	int *sMR = new int[nLR + 3], *saMR = new int[nLR + 3];

	// assign triples
	for (int i = 0; i < 3; i++)
		sMR[i] = saMR[i] = 0;

	// radix sort those not divisible by 3
	for (int i = 0, j = 0; i < n + nL - nM; i++)
		if (i % 3 != 0)
			sMR[j++] = i;
	// "swap" indices
	radixSort(sMR, saMR, s + 2, nLR, K);
	radixSort(saMR, sMR, s + 1, nLR, K);
	radixSort(sMR, saMR, s, nLR, K);

	// assign bucket names
	int name = 0;
	c[0] = c[1] = c[2] = -1;
	for (int i = 0; i < nLR; i++) {
		bool equal = true;
		for (int j = 0; j < 3; j++) {
			equal &= (c[j] == s[saMR[i] + j]);
			c[j] = s[saMR[i] + j];
		}
		if (!equal) name++;
		// key assignment
		if (saMR[i] % 3 == 1)
			sMR[saMR[i] / 3] = name;
		else
			sMR[saMR[i] / 3 + nL] = name;
	}

	// ternary partition
	if (name < nLR) {
		DC3algorithm(sMR, saMR, nLR, name);
		for (int i = 0; i < nLR; i++)
			sMR[saMR[i]] = i + 1;
	}
	else {
		for (int i = 0; i < nLR; i++)
			saMR[sMR[i] - 1] = i;
	}
	for (int i = 0, j = 0; i < nLR; i++)
		if (saMR[i] < nL)
			sL[j++] = 3 * saMR[i];
	radixSort(sL, saL, s, nL, K);
	for (int p = 0, t = nL - nM, k = 0; k < n; k++) {
		#define GI (saMR[t] < nL ? 3*saMR[t] + 1 : 3*(saMR[t] - nL) + 2)
		int i = GI;
		int j = saL[p];
		if (saMR[t] < nL ?
			leq(s[i], sMR[saMR[t] + nL], s[j], sMR[j / 3]) :
			leq(s[i], s[i + 1], sMR[saMR[t] - nL + 1], s[j], s[j + 1], sMR[j / 3 + nL])
		) {
			sa[k] = i;
			if (++t == nLR)
				for (k++; p < nL; p++, k++)
					sa[k] = saL[p];
		}
		else {
			sa[k] = j;
			if (++p == nL)
				for (k++; t < nLR; t++, k++)
					sa[k] = GI;
		}
		#undef GI
	}
	delete[] sL, saL, sMR, saMR;
}
int n, g, q;
// driver function build SA
void buildSA(char str[]) {
	// int n = strlen(str);
	char mn = 127, mx = 0;
	for (int i = 0; i < n; i++) {
		mn = min(mn, str[i]);
		mx = max(mx, str[i]);
	}
	for (int i = 0; i < n; i++)
		sn[i] = str[i] - mn + 1;
	sn[n] = sn[n+1] = sn[n+2] = 0;
	DC3algorithm(sn, sa, n, mx - mn + 1);
	for (int i = 0; i < n; i++)
		pos[sa[i]] = i;
}
// least common prefix, assuming sa is built
void buildLCP(char s[]) {
	for (int i = 0, k = 0; i < n; ++i) {
		if (pos[i] != n - 1) {
			for (int j = sa[pos[i] + 1]; s[i+k] == s[j+k]; ++k);
			lcp[pos[i]] = k;
			if (k) --k;
		}
		else lcp[pos[i]] = 0;
	}
}
int st[20][N], lg[N];
// build min sparse table for lcp
void buildSparse() {
	for (int i = 2; i < n; ++i)
		lg[i] = lg[i >> 1] + 1;
	memcpy(st[0], lcp, sizeof(int) * n);
	for (int j = 0; (2 << j) <= n; ++j) {
		for (int i = 0; i + (2 << j) <= n; ++i) {
			int k = i + (1 << j);
			st[j+1][i] = min(st[j][i], st[j][k]);
		}
	}
}
int querySparse(int a, int b) {
	if (b < a) swap(a, b);
	int k = lg[b - a + 1];
	int ab = b - (1 << k) + 1;
	return min(st[k][a], st[k][ab]);
}

int sqrtn;
int offl[N >> 1], offr[N >> 1], offpl[N >> 1], offpr[N >> 1];

inline bool queryCmp(int a, int b) {
	return (offpl[a] / sqrtn < offpl[b] / sqrtn) || (offpl[a] / sqrtn == offpl[b] / sqrtn && offpr[a] < offpr[b]);
}

pair<int, int> range(int mid, int k) {
	// find the range of LCPs within k
	int prefix; {
		int L = 0, R = mid;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(M, mid - 1) < k) {
				L = M + 1;
			} else {
				R = M;
			}
		}
		prefix = L;
	}
	int suffix; {
		int L = mid, R = n - 1;
		while (L < R) {
			int M = (L + R) / 2;
			if (querySparse(mid, M + 1) < k) {
				R = M;
			} else {
				L = M + 1;
			}
		}
		suffix = L + 1;
	}
	return make_pair(prefix, suffix);
}

int Q[N >> 1];
int ans[N >> 1], ansmx[N >> 1];
int tree[N], leaf[N];

void prepare() {
	++g;
	for (int i = 0; i < g; ++i)
		tree[i + g] = i;
	for (int i = 2 * (g+1) - 1; i > 0; --i)
		tree[i >> 1] = min(tree[i], tree[i ^ 1]);
}

void add(int i, int x) {
	for (leaf[i] += x, i += g; i > 0; i >>= 1) {
		int l1 = leaf[tree[i]];
		int l2 = leaf[tree[i ^ 1]];
		tree[i >> 1] = (l1 > l2 || (l1 == l2 && tree[i] < tree[i ^ 1]) ? tree[i] : tree[i ^ 1]);
	}
}

int queryTree(int a, int b) {
	int ans = tree[a + g];
	for (a += g, b += g + 1; a < b; a >>= 1, b >>= 1) {
		if (a & 1) {
			// cout << "Found: " << a << ' ' << tree[a] << ' ' << leaf[tree[a]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[a]] || (leaf[ans] == leaf[tree[a]] && tree[a] < ans))
				ans = tree[a];
			++a;
		}
		if (b & 1) {
			--b;
			// cout << "Found: " << b << ' ' << tree[b] << ' ' << leaf[tree[b]] << endl;
			if (ans == -1 || leaf[ans] < leaf[tree[b]] || (leaf[ans] == leaf[tree[b]] && tree[b] < ans))
				ans = tree[b];
		}
		// cout << "at tree: " << a << ' ' << b << ' ' << ans << endl;
	}
	return ans;
}

int insert(int id) {
	int g = grp[sa[id]];
	add(g, 1);
}

int remove(int id) {
	int g = grp[sa[id]];
	add(g, -1);
}

void mo() {
	prepare();
	sqrtn = sqrt(n) + 1;
	for (int i = 0; i < n; ++i)
		Q[i] = i;
	sort(Q, Q + q, queryCmp);
	int L = 0, R = -1;
	for (int i = 0; i < q; ++i) {
		int id = Q[i];
		int pl = offpl[id];
		int pr = offpr[id];
		int l = offl[id];
		int r = offr[id];
		// cout << "my query: " << id << ' ' << pl << ' ' << pr << ' ' << l << ' ' << r << endl;
		while (R < pr) insert(++R);
		while (L > pl) insert(--L);
		while (R > pr) remove(R--);
		while (L < pl) remove(L++);
		// for (int i = l; i <= r; ++i)
			// cout << i << ": " << leaf[i] << endl;
		ans[id] = queryTree(l, r);
		ansmx[id] = leaf[ans[id]];
	}
}

int main() {
	scanf("%s", s);
	n = strlen(s);
	s[n++] = SENTI;
	fill(grp, grp + n, 0);
	scanf("%d", &g);
	for (int i = 0; i < g; ++i) {
		scanf("%s", s + n);
		int len = strlen(s + n);
		s[n + len] = SENTI;
		fill(grp + n, grp + n + len + 1, i + 1);
		n += len + 1;
	}
	buildSA(s);
	buildLCP(s);
	buildSparse();
	// for (int i = 0; i < n; ++i)
		// cout << i << ": " << sa[i] << " " << grp[sa[i]] << " " << s + sa[i] << endl;
	scanf("%d", &q);
	for (int i = 0; i < q; ++i) {
		int pl, pr;
		scanf("%d%d%d%d", offl + i, offr + i, &pl, &pr);
		pair<int, int> r = range(pos[pl - 1], pr - pl + 1); // O(log n)
		offpl[i] = r.first;
		offpr[i] = r.second;
		// cout << "My query " << pl << ' ' << pr << ": " << endl;
		// cout << r.first << ' ' << r.second << endl;
		// for (int i = pl - 1; i < pr; ++i)
			// cout << s[i]; cout << endl;
		// cout << r.first << ' ' << r.second << endl;
	}
	mo();
	for (int i = 0; i < q; ++i)
		printf("%d %d\n", ans[i], ansmx[i]);
}


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define pb push_back
#define MAXN 2000000
#define MP make_pair
#define PA pair<int,int>

struct tnode
{
    int l,r,val,col;
}c[MAXN*10];

int T=1,cs;

struct SAM
{
	struct node
	{
		int ch[27];
		int par,val;
		node (int val=0):par(0),val(val)
		{
			memset(ch,0,sizeof(ch));
		}
	}sam[MAXN];
	int last,m,n;
	int sz[MAXN],rs[MAXN],h[MAXN],b[MAXN];
	void append(int w)
	{
		int p=last,np,q,nq;
		sam[last=np=++m]=node(sam[p].val+1);
		rs[np]=1;
		while (p&&sam[p].ch[w]==0)
			sam[p].ch[w]=np,p=sam[p].par;
		if (p==0)
		{
			sam[np].par=1;
			return;
		}
		q=sam[p].ch[w];
		if (sam[p].val+1==sam[q].val) sam[np].par=q;
		else
		{
			sam[nq=++m]=sam[q];
			sam[nq].val=sam[p].val+1;
			sam[q].par=sam[np].par=nq;
			while (p&&sam[p].ch[w]==q)
				sam[p].ch[w]=nq,p=sam[p].par;
		}
	}
	void init_sam()
	{
		for (int i=1;i<=m;i++)
			++h[sam[i].val];
		for (int i=1;i<=n;i++)
			h[i]+=h[i-1];
		for (int i=m;i>0;i--)
			b[h[sam[i].val]--]=i;
		for (int i=m;i>0;i--)
		{
			int u=b[i];
			if (T) rs[sam[u].par]+=rs[u];
			else rs[u]=1;
		}
		rs[1]=0;
		for (int i=m;i>=1;i--)
		{
			int u=b[i];
			sz[u]=rs[u];
			for (int j=0;j<27;j++)
				sz[u]+=sz[sam[u].ch[j]];
		}
	}
}sa;

char s[MAXN],t[MAXN];
int pos[MAXN],fa[MAXN][20],n,m,q[MAXN],root[MAXN],id[MAXN],sum[MAXN];

int find(int d,int len)
{
    while(1)
    {
      int k=0;
      while(sa.sam[fa[d][k]].val>=len)k++;
      k--;
      if(k==-1)break;
      else d=fa[d][k];
    }
    return d;
}

void insert(int &node,int le,int ri,int w)
{
     if(!node)node=++cs;
     if(le<=w && ri>=w)
     {
       if(le==ri)
       {
         c[node].val++;
         c[node].col=w;
         return;
       }
       int mid=le+ri>>1;
       insert(c[node].l,le,mid,w);
       insert(c[node].r,mid+1,ri,w);
       if(c[c[node].l].val>c[node].val)
       {
         c[node].val=c[c[node].l].val;
         c[node].col=c[c[node].l].col;
       }
       if(c[c[node].r].val>c[node].val)
       {
         c[node].val=c[c[node].r].val;
         c[node].col=c[c[node].r].col;
       }
     }
}

int merge(int x,int y,int le,int ri)
{
    if(!x || !y)return x|y;
    int node=++cs;
    if(le==ri)
    {
      c[node].val=c[x].val+c[y].val;
      c[node].col=le;
      return node;
    }
    int mid=le+ri>>1;
    c[node].l=merge(c[x].l,c[y].l,le,mid);
    c[node].r=merge(c[x].r,c[y].r,mid+1,ri);
    c[node].val=max(c[c[node].l].val,c[c[node].r].val);
    if(c[node].val==c[c[node].l].val)c[node].col=c[c[node].l].col;
    else c[node].col=c[c[node].r].col;
    return node;
}

PA query(int node,int le,int ri,int ql,int qr)
{
    if(!node)return MP(0,0);
    if(le<=qr && ri>=ql)
    {
      if(le>=ql && ri<=qr)return MP(c[node].val,c[node].col);
      else
      {
          int mid=le+ri>>1;
          PA pa1=query(c[node].l,le,mid,ql,qr),pa2=query(c[node].r,mid+1,ri,ql,qr);
          if(pa1.first<pa2.first)return pa2;
          else return pa1;
      }
    }
    else return MP(0,0);
}

int main()
{
    scanf("%s",s+1);
    sa.last=sa.m=1;
    int lens=strlen(s+1);
    for(int j=1;j<=lens;j++)
    {
      sa.n++;
      sa.append(s[j]-'a');
      pos[j]=sa.last;
    }
    sa.n++;
    sa.append(26);
    int lensm=sa.m;
    scanf("%d",&n);
    rep(i,1,n)
    {
      scanf("%s",t+1);
      int lent=strlen(t+1);
      for(int j=1;j<=lent;j++)
      {
        sa.n++;
        sa.append(t[j]-'a');
        insert(root[sa.last],1,n,i);
      }
      sa.n++;
      sa.append(26);
    }
    sa.init_sam();

    rep(i,1,sa.m)sum[sa.sam[i].val]++;
    rep(i,1,sa.m)sum[i]+=sum[i-1];
    rep(i,1,sa.m)id[sum[sa.sam[i].val]--]=i;

    for(int i=sa.m;i>0;i--)
    if(sa.sam[id[i]].par)
    {
      root[sa.sam[id[i]].par]=merge(root[id[i]],root[sa.sam[id[i]].par],1,n);
    }
    rep(i,1,sa.m)fa[i][0]=sa.sam[i].par;
    rep(k,1,18)
      rep(i,1,sa.m)
        fa[i][k]=fa[fa[i][k-1]][k-1];
    scanf("%d",&m);
    rep(i,1,m)
    {
      int ql,qr,pl,pr;
      scanf("%d%d%d%d",&ql,&qr,&pl,&pr);
      q[i]=find(pos[pr],pr-pl+1);
      PA pa=query(root[q[i]],1,n,ql,qr);
      if(pa.first)printf("%d %d\n",pa.second,pa.first);
      else printf("%d %d\n",ql,0);
      //cout<<node<<endl;
    }
    return 0;
}


#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<bitset>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#include<cstring>
#include<vector>
#define rep(i,j,k) for(register int i = j; i <= k; i++)
#define dow(i,j,k) for(register int i = j; i >= k; i--)
#define ull unsigned long long
#define ll long long
#define eps 1e-5
using namespace std;

inline int read() {
	int s = 0, t = 1; char c = getchar();
	while( !isdigit(c) ) { if( c == '-' ) t = -1; c = getchar(); }
	while( isdigit(c) ) s = s * 10 + c - 48, c = getchar();
	return s * t;
}

const int N = 2e6+5;
inline void Max(int &x,int v) { if( v > x ) x = v; }
int son[N][26], dep[N], f[N], tot = 1, pos[N], last = 1, u, v, nv, p;
inline void extend(int c) {
	u = last;
	if( v = son[u][c] ) {
		if( dep[v] == dep[u] + 1 ) last = v;
		else {
			last = nv = ++tot, dep[nv] = dep[u] + 1, f[nv] = f[v], f[v] = nv;
			memcpy(son[nv],son[v],sizeof(son[v]));
			while( son[u][c] == v ) son[u][c] = nv, u = f[u];
		}
	} else {
		last = p = ++tot, dep[p] = dep[u] + 1;
		while( u && !son[u][c] ) son[u][c] = p, u = f[u];
		if( !u ) f[p] = 1;
		else {
			if( dep[v=son[u][c]] == dep[u] + 1 ) f[p] = v;
			else {
				nv = ++tot, dep[nv] = dep[u] + 1, f[nv] = f[v], f[v] = f[p] = nv;
				memcpy(son[nv],son[v],sizeof(son[v]));
				while( son[u][c] == v ) son[u][c] = nv, u = f[u];
			}
		}
	}
}

const int M = 20000000;
struct node{ int maxl, id; node(int a=0,int b=0) { maxl = a, id = b;} } A[M];
int rt[N], ql, qr, ch[M][2], cnt = 0, fa[21][N];

char c[N]; int n, q[N], cs[N], bit;
inline void sort() {
	int maxl = 1;
	rep(i,1,tot) cs[dep[i]]++, Max(maxl,dep[i]);
	rep(i,1,maxl) cs[i] += cs[i-1];
	dow(i,tot,1) q[cs[dep[i]]--] = i;
	bit = log2(maxl);
}

#define lc ch[k][0]
#define rc ch[k][1]
inline node operator + (const node&a,const node&b) {
	if( a.maxl < b.maxl ) return b;
	else return a;
}
inline void up(int k) {
	if( lc && rc ) A[k] = A[lc] + A[rc];
	else if( lc ) A[k] = A[lc];
	else A[k] = A[rc];
}
inline void insert(int l,int r,int &k) {
	if( !k ) k = ++cnt;
	if( l == r ) { A[k] = node(A[k].maxl+1,l); return; }
	int mid = (l + r) >> 1;
	if( ql <= mid ) insert(l,mid,lc);
	else insert(mid+1,r,rc);
	up(k);
}
inline node query(int l,int r,int k) {
	if( !k ) return node(0,max(l,ql));
	if( ql <= l && r <= qr ) return A[k];
	int mid = (l + r) >> 1;
	if( qr <= mid ) return query(l,mid,lc);
	else if( ql > mid ) return query(mid+1,r,rc);
	else return query(l,mid,lc) + query(mid+1,r,rc);
}
#define lpc ch[pre][0]
#define rpc ch[pre][1]
inline int merge(int l,int r,int k,int pre) {
	if( !k || !pre ) return k | pre;
	if( l == r ) { A[k] = node(A[k].maxl+A[pre].maxl,l); return k; }
	int mid = (l + r) >> 1;
	lc = merge(l,mid,lc,lpc);
	rc = merge(mid+1,r,rc,rpc);
	return up(k), k;
}

inline int get(int x,int y) {
	int len = y - x + 1; x = pos[y];
	dow(i,bit,0) if( dep[fa[i][x]] >= len ) x = fa[i][x];
	return x;
}

int main() {
//	freopen("in.txt","r",stdin);
	scanf("%s",c+1);
	n = strlen(c+1);
	rep(i,1,n) extend(c[i]-'a'), pos[i] = last;
	int m = read();
//	cout<<(c+1)<<endl;
	rep(i,1,m) {
		last = 1;
		scanf("%s",c+1);
		n = strlen(c+1);
//		cout<<"F "<<n<<" "<<(c+1)<<endl;
		rep(j,1,n) extend(c[j]-'a'), ql = i, insert(1,m,rt[last]);
	}
	sort();
//	rep(i,1,tot) cout<<f[i]<<" "; cout<<endl;
//	rep(i,1,tot) cout<<dep[i]<<" "; cout<<endl;
	dow(i,tot,1) if( f[q[i]] ) rt[f[q[i]]] = merge(1,m,rt[f[q[i]]],rt[q[i]]);
	rep(i,1,tot) fa[0][i] = f[i];
	rep(i,1,bit) rep(j,1,tot) fa[i][j] = fa[i-1][fa[i-1][j]];

	int q = read(), x, y;
	while( q-- ) {
		ql = read(), qr = read(), x = read(), y = read();
		x = get(x,y);
		node a = query(1,m,rt[x]);
		printf("%d %d\n", a.id,a.maxl);
	}
	return 0;
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace D
{
    class MainClass
    {
        class MinHeap<T> where T : IComparable<T>
        {
            public List<T> array = new List<T>();

            public void Add(T element)
            {
                array.Add(element);
                int c = array.Count - 1;
                while (c > 0 && array[c].CompareTo(array[c / 2]) == -1)
                {
                    T tmp = array[c];
                    array[c] = array[c / 2];
                    array[c / 2] = tmp;
                    c = c / 2;
                }
            }

            public void ReplaceMin(T element)
            {
                array[0] = element;
                int c = 0;
                while (c < array.Count)
                {
                    int min = c;
                    if (2 * c + 1 < array.Count && array[2 * c + 1].CompareTo(array[min]) == -1)
                        min = 2 * c + 1;
                    if (2 * c + 2 < array.Count && array[2 * c + 2].CompareTo(array[min]) == -1)
                        min = 2 * c + 2;

                    if (min == c)
                        break;
                    else
                    {
                        T tmp = array[c];
                        array[c] = array[min];
                        array[min] = tmp;
                        c = min;
                    }
                }
            }

            public T RemoveMin()
            {
                T ret = array[0];
                array[0] = array[array.Count - 1];
                array.RemoveAt(array.Count - 1);

                int c = 0;
                while (c < array.Count)
                {
                    int min = c;
                    if (2 * c + 1 < array.Count && array[2 * c + 1].CompareTo(array[min]) == -1)
                        min = 2 * c + 1;
                    if (2 * c + 2 < array.Count && array[2 * c + 2].CompareTo(array[min]) == -1)
                        min = 2 * c + 2;

                    if (min == c)
                        break;
                    else
                    {
                        T tmp = array[c];
                        array[c] = array[min];
                        array[min] = tmp;
                        c = min;
                    }
                }

                return ret;
            }

            public T Peek()
            {
                return array[0];
            }

            public int Count
            {
                get
                {
                    return array.Count;
                }
            }
        }

        private static void ReadTwoInts(out int a, out int b)
        {
            var input = Console.ReadLine().Split();
            a = int.Parse(input[0]);
            b = int.Parse(input[1]);
        }

        public static void Main(string[] args)
        {
            int n, k;
            ReadTwoInts(out n, out k);
            Solve(n, k);
        }

        private static void Solve(int n, int k)
        {
            var servers = new MinHeap<long>();
            servers.array = new List<long>(new long[k]);
            var times = new long[n];
            int s, m;

            for (int i = 0; i < n; i++)
            {
                ReadTwoInts(out s, out m);
                var min = Math.Max(servers.Peek(), s) + m;
                servers.ReplaceMin(min);
                times[i] = min;
            }

            foreach (var time in times)
            {
                Console.WriteLine(time);
            }
        }
    }
}


using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace vkcup
{
    class Program
    {
        private readonly static Thread _writerThread = new Thread(WriterThread) { IsBackground = false };
        private readonly static ConcurrentQueue<string> _lines = new ConcurrentQueue<string>();

        private volatile static bool _isTerminated;

        static void Main(string[] args)
        {
            //var sw = new Stopwatch();
            //sw.Start();

            //var rnd = new Random();
            //uint minGetCounter = 0;

            uint n, k;

            {
                var nk = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                n = nk[0];
                k = nk[1];

                //
                //n = 500000;
                //k = 150000;
            }

            var workerReleaseTimes = new SortedSet<ulong>();
            var workerReleaseTimesDictionary = new Dictionary<ulong, uint>((int)k);

            ulong min;

            {
                workerReleaseTimes.Add(0);
                workerReleaseTimesDictionary.Add(0, k);

                min = 0;
            }

            _writerThread.Start();

            for (var i = 0; i < n; ++i)
            {
                var sm = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                var s = sm[0];
                var m = sm[1];

                //var s = (uint)i;
                //var m = (uint)rnd.Next(100000000, 1000000000);

                //

                var count = workerReleaseTimesDictionary[min] - 1;

                if (count == 0)
                {
                    workerReleaseTimes.Remove(min);
                    workerReleaseTimesDictionary.Remove(min);
                }
                else
                    workerReleaseTimesDictionary[min] = count;

                var releaseTime = min <= s ? s + m : min + m;

                uint releaseTimeWorkerCount;

                if (workerReleaseTimesDictionary.TryGetValue(releaseTime, out releaseTimeWorkerCount))
                    ++workerReleaseTimesDictionary[releaseTime];
                else
                {
                    workerReleaseTimes.Add(releaseTime);
                    workerReleaseTimesDictionary.Add(releaseTime, 1);
                }

                if (count == 0)
                {
                    min = workerReleaseTimes.Min;
                    //++minGetCounter;
                }

                if (min > releaseTime)
                    min = releaseTime;

                CWriteLine(releaseTime);
            }

            //sw.Stop();

            //Console.WriteLine("total elapsed ms: " + sw.ElapsedMilliseconds);
            //Console.WriteLine("minGetCount: " + minGetCounter);
            //Console.ReadKey();

            _isTerminated = true;
        }

        private static void CWriteLine(object o)
        {
            _lines.Enqueue(o.ToString());
        }

        private static void WriterThread(object o)
        {
            string line;

            while (!_isTerminated)
            {
                while (_lines.TryDequeue(out line))
                    Console.WriteLine(line);
                Thread.Sleep(50);
            }

            while (_lines.TryDequeue(out line))
                Console.WriteLine(line);
        }

        //static void Main(string[] args)
        //{
        //    var sw = new Stopwatch();
        //    sw.Start();

        //    var rnd = new Random();
        //    uint minGetCounter = 0;

        //    uint n, k;

        //    {
        //        //var nk = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

        //        //n = nk[0];
        //        //k = nk[1];

        //        n = 5000000;
        //        k = 1500000;
        //    }

        //    var workerReleaseTimes = new SortedList<ulong, uint>();
        //    //var workerReleaseTimesDictionary = new Dictionary<ulong, uint>((int)k);

        //    ulong min;

        //    {
        //        workerReleaseTimes.Add(0, k);

        //        min = 0;
        //    }

        //    for (var i = 0; i < n; ++i)
        //    {
        //        //var sm = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

        //        //var s = sm[0];
        //        //var m = sm[1];

        //        var s = (uint)i;
        //        uint m = (uint)rnd.Next(10000000, 1000000000);

        //        //

        //        var first = workerReleaseTimes.First();

        //        var count = first.Value - 1;

        //        if (count == 0)
        //            workerReleaseTimes.Remove(first.Key);
        //        else
        //            workerReleaseTimes[first.Key] = count;

        //        var releaseTime = min <= s ? s + m : min + m;

        //        uint releaseTimeWorkerCount;

        //        if (workerReleaseTimes.TryGetValue(releaseTime, out releaseTimeWorkerCount))
        //            ++workerReleaseTimes[releaseTime];
        //        else
        //            workerReleaseTimes.Add(releaseTime, 1);

        //        if (count == 0)
        //        {
        //            min = workerReleaseTimes.First().Value;
        //            ++minGetCounter;
        //        }

        //        if (min > releaseTime)
        //            min = releaseTime;

        //        //Console.WriteLine(releaseTime);
        //    }

        //    sw.Stop();

        //    Console.WriteLine("total elapsed ms: " + sw.ElapsedMilliseconds);
        //    Console.WriteLine("minGetCount: " + minGetCounter);
        //    Console.ReadKey();
        //}
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace vkcup
{
    class Program
    {
        static void Main(string[] args)
        {
            uint n, k;

            {
                var nk = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                n = nk[0];
                k = nk[1];
            }

            var workerReleaseTimes = new SortedSet<SetEntry>(new SetComparer());
            var workerReleaseTimesDictionary = new Dictionary<uint, SetEntry>();

            {
                var firstSetEntry = new SetEntry(0, k);

                workerReleaseTimes.Add(firstSetEntry);
                workerReleaseTimesDictionary.Add(firstSetEntry.ReleaseTime, firstSetEntry);
            }

            for (var i = 0; i < n; ++i)
            {
                var sm = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                var s = sm[0];
                var m = sm[1];

                var min = workerReleaseTimes.Min;

                --min.WorkerCount;

                if (min.WorkerCount == 0)
                {
                    workerReleaseTimes.Remove(min);
                    workerReleaseTimesDictionary.Remove(min.ReleaseTime);
                }

                var releaseTime = min.ReleaseTime <= s ? s + m : min.ReleaseTime + m;

                SetEntry releaseEntry;

                if (workerReleaseTimesDictionary.TryGetValue(releaseTime, out releaseEntry))
                    ++releaseEntry.WorkerCount;
                else
                {
                    releaseEntry = new SetEntry(releaseTime, 1);

                    workerReleaseTimes.Add(releaseEntry);
                    workerReleaseTimesDictionary.Add(releaseEntry.ReleaseTime, releaseEntry);
                }

                Console.WriteLine(releaseTime);
            }
        }

        private class SetEntry
        {
            public readonly uint ReleaseTime;
            public uint WorkerCount;

            public SetEntry(uint releaseTime, uint workerCount)
            {
                ReleaseTime = releaseTime;
                WorkerCount = workerCount;
            }

            public override bool Equals(object obj)
            {
                if (!(obj is SetEntry))
                    return false;

                return ((SetEntry)obj).ReleaseTime == ReleaseTime;
            }
        }

        private class SetComparer : IComparer<SetEntry>
        {
            public int Compare(SetEntry x, SetEntry y)
            {
                return x.ReleaseTime.CompareTo(y.ReleaseTime);
            }
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace vkcup
{
    class Program
    {
        static void Main(string[] args)
        {
            uint n, k;

            {
                var nk = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                n = nk[0];
                k = nk[1];
            }

            var workerReleaseTimes = new SortedSet<SetEntry>(new SetComparer());
            var workerReleaseTimesDictionary = new Dictionary<ulong, SetEntry>();

            {
                var firstSetEntry = new SetEntry(0, k);

                workerReleaseTimes.Add(firstSetEntry);
                workerReleaseTimesDictionary.Add(firstSetEntry.ReleaseTime, firstSetEntry);
            }

            for (var i = 0; i < n; ++i)
            {
                var sm = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                var s = sm[0];
                var m = sm[1];

                var min = workerReleaseTimes.Min;

                --min.WorkerCount;

                if (min.WorkerCount == 0)
                {
                    workerReleaseTimes.Remove(min);
                    workerReleaseTimesDictionary.Remove(min.ReleaseTime);
                }

                var releaseTime = min.ReleaseTime <= s ? s + m : min.ReleaseTime + m;

                SetEntry releaseEntry;

                if (workerReleaseTimesDictionary.TryGetValue(releaseTime, out releaseEntry))
                    ++releaseEntry.WorkerCount;
                else
                {
                    releaseEntry = new SetEntry(releaseTime, 1);

                    workerReleaseTimes.Add(releaseEntry);
                    workerReleaseTimesDictionary.Add(releaseEntry.ReleaseTime, releaseEntry);
                }

                Console.WriteLine(releaseTime);
            }
        }

        private class SetEntry
        {
            public readonly ulong ReleaseTime;
            public uint WorkerCount;

            public SetEntry(ulong releaseTime, uint workerCount)
            {
                ReleaseTime = releaseTime;
                WorkerCount = workerCount;
            }

            public override bool Equals(object obj)
            {
                if (!(obj is SetEntry))
                    return false;

                return ((SetEntry)obj).ReleaseTime == ReleaseTime;
            }
        }

        private class SetComparer : IComparer<SetEntry>
        {
            public int Compare(SetEntry x, SetEntry y)
            {
                return x.ReleaseTime.CompareTo(y.ReleaseTime);
            }
        }
    }
}


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace vkcup
{
    class Program
    {
        static void Main(string[] args)
        {
            //var sw = new Stopwatch();
            //sw.Start();

            //var rnd = new Random();
            //uint minGetCounter = 0;

            uint n, k;

            {
                var nk = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                n = nk[0];
                k = nk[1];

                //
                //n = 500000;
                //k = 50000;
            }

            var workerReleaseTimes = new SortedSet<ulong>();
            var workerReleaseTimesDictionary = new Dictionary<ulong, uint>((int)k);

            ulong min;

            {
                workerReleaseTimes.Add(0);
                workerReleaseTimesDictionary.Add(0, k);

                min = 0;
            }

            for (var i = 0; i < n; ++i)
            {
                var sm = Console.ReadLine().Split(' ').Select(uint.Parse).ToList();

                var s = sm[0];
                var m = sm[1];

                //
                //var s = (uint)i;
                //uint m = (uint)rnd.Next(10000000, 1000000000);

                var count = workerReleaseTimesDictionary[min] -  1;

                if (count == 0)
                {
                    workerReleaseTimes.Remove(min);
                    workerReleaseTimesDictionary.Remove(min);
                }
                else
                    workerReleaseTimesDictionary[min] = count;

                var releaseTime = min <= s ? s + m : min + m;

                uint releaseTimeWorkerCount;

                if (workerReleaseTimesDictionary.TryGetValue(releaseTime, out releaseTimeWorkerCount))
                    ++workerReleaseTimesDictionary[releaseTime];
                else
                {
                    workerReleaseTimes.Add(releaseTime);
                    workerReleaseTimesDictionary.Add(releaseTime, 1);
                }

                if (count == 0)
                {
                    min = workerReleaseTimes.Min;
                    //++minGetCounter;
                }

                if (min > releaseTime)
                    min = releaseTime;

                Console.WriteLine(releaseTime);
            }

            //sw.Stop();

            //Console.WriteLine("total elapsed ms: " + sw.ElapsedMilliseconds);
            //Console.WriteLine("minGetCount: " + minGetCounter);
            //Console.ReadKey();
        }

        //private class SetEntry
        //{
        //    public readonly ulong ReleaseTime;
        //    public uint WorkerCount;

        //    public SetEntry(ulong releaseTime, uint workerCount)
        //    {
        //        ReleaseTime = releaseTime;
        //        WorkerCount = workerCount;
        //    }

        //    public override bool Equals(object obj)
        //    {
        //        if (!(obj is SetEntry))
        //            return false;

        //        return ((SetEntry)obj).ReleaseTime == ReleaseTime;
        //    }
        //}

        //private class SetComparer : IComparer<SetEntry>
        //{
        //    public int Compare(SetEntry x, SetEntry y)
        //    {
        //        return x.ReleaseTime.CompareTo(y.ReleaseTime);
        //    }
        //}
    }
}