#include <bits/stdc++.h>
using namespace std;
template <class T>
inline bool checkMax(T &a, const T &b) {
  return a < b ? a = b, 1 : 0;
}
template <typename T, typename... Args>
inline void checkMax(T &a, const Args... arg) {
  checkMax(a, max(arg...));
}
template <class T>
inline bool checkMin(T &a, const T &b) {
  return a > b ? a = b, 1 : 0;
}
template <typename T, typename... Args>
inline void checkMin(T &a, const Args... arg) {
  checkMin(a, min(arg...));
}
const int INF = 0x3f3f3f3f;
const long long llINF = 1e18;
const int MAXN = 1e5 + 5;
const int MAXM = 5e5 + 5;
const int LOG = 19;
int n, m, Q, Test;
int rt[MAXN];
char s[MAXM], t[MAXN];
struct Node {
  int x, y;
  Node(int _x = 0, int _y = 0) : x(_x), y(_y) {}
  bool operator<(const Node &a) const { return x == a.x ? y > a.y : x < a.x; }
};
struct SegmentTree {
  static const int MAXL = MAXN * 30;
  int tot;
  int ls[MAXL], rs[MAXL];
  Node val[MAXL];
  void update(int k) { val[k] = max(val[ls[k]], val[rs[k]]); }
  void modify(int &k, int l, int r, int x) {
    if (!k) k = ++tot;
    if (l == r) {
      val[k].x += 1;
      val[k].y = l;
      return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid)
      modify(ls[k], l, mid, x);
    else
      modify(rs[k], mid + 1, r, x);
    update(k);
  }
  Node query(int k, int l, int r, int ql, int qr) {
    if (!k) return Node(0, 0);
    if (l >= ql && r <= qr) return val[k];
    int mid = (l + r) >> 1;
    Node res(0, 0);
    if (ql <= mid) checkMax(res, query(ls[k], l, mid, ql, qr));
    if (qr > mid) checkMax(res, query(rs[k], mid + 1, r, ql, qr));
    return res;
  }
  int merge(int x, int y, int l, int r) {
    if (!x || !y) return x | y;
    int k = ++tot;
    if (l == r) {
      val[k] = val[x];
      val[k].x += val[y].x;
      return k;
    }
    int mid = (l + r) >> 1;
    ls[k] = merge(ls[x], ls[y], l, mid);
    rs[k] = merge(rs[x], rs[y], mid + 1, r);
    update(k);
    return k;
  }
} T;
struct SAM {
  int last, tot;
  int son[MAXN][26], len[MAXN], fa[MAXN], maxlen[MAXM], lstpos[MAXM],
      f[MAXN][LOG];
  vector<int> G[MAXN];
  SAM() { last = tot = 1; }
  void extend(int c, int id) {
    static int p, q, np, nq;
    p = last;
    last = ++tot;
    np = tot;
    len[np] = len[p] + 1;
    T.modify(rt[np], 1, m, id);
    for (; p && !son[p][c]; p = fa[p]) son[p][c] = np;
    if (!p) {
      fa[np] = 1;
      return;
    }
    q = son[p][c];
    if (len[q] == len[p] + 1) {
      fa[np] = q;
      return;
    }
    nq = ++tot;
    memcpy(son[nq], son[q], sizeof(son[nq]));
    len[nq] = len[p] + 1;
    fa[nq] = fa[q];
    fa[q] = fa[np] = nq;
    for (; p && son[p][c] == q; p = fa[p]) son[p][c] = nq;
  }
  void init() {
    for (int i = 2; i <= tot; i++) G[fa[i]].push_back(i);
    int p = 1, l = 0;
    for (int i = 1; i <= n; i++) {
      int c = (s[i] - 'a');
      while (p && !son[p][c]) p = fa[p], l = len[p];
      if (son[p][c])
        p = son[p][c], l += 1;
      else
        p = 1, l = 0;
      lstpos[i] = p;
      maxlen[i] = l;
    }
  }
  void dfs(int u, int father) {
    f[u][0] = father;
    for (int i = 1; i < LOG; i++) f[u][i] = f[f[u][i - 1]][i - 1];
    for (int v : G[u]) {
      dfs(v, u);
      rt[u] = T.merge(rt[u], rt[v], 1, m);
    }
  }
  Node query(int l, int r, int pl, int pr) {
    if (pr - pl + 1 > maxlen[pr]) return {0, l};
    int p = lstpos[pr];
    for (int i = LOG - 1; i >= 0; i--)
      if (len[f[p][i]] >= pr - pl + 1) p = f[p][i];
    Node res = T.query(rt[p], 1, m, l, r);
    if (!res.x) res.y = l;
    return res;
  }
} sam;
int main() {
  scanf("%s%d", s + 1, &m);
  n = strlen(s + 1);
  for (int i = 1; i <= m; i++) {
    scanf("%s", t + 1);
    int len = strlen(t + 1);
    sam.last = 1;
    for (int j = 1; j <= len; j++) sam.extend(t[j] - 'a', i);
  }
  sam.init();
  sam.dfs(1, 0);
  scanf("%d", &Q);
  while (Q--) {
    Test += 1;
    int l, r, pl, pr;
    scanf("%d%d%d%d", &l, &r, &pl, &pr);
    Node ans = sam.query(l, r, pl, pr);
    printf("%d %d\n", ans.y, ans.x);
  }
  return 0;
}
