/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author
 */

#include <bits/stdc++.h>
using namespace std;
#define long long long
const long MOD = 1e9+7, LINF = 1e18 + 1e16;
const int INF = 1e9+1;
const double EPS = 1e-10;
const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 4e5+10;

struct node {
    int bound, type, l, r, id;
    node(int bound, int type, int l, int r, int id): bound(bound), type(type), l(l), r(r), id(id) {}
    bool operator<(const node &rhs) const {
        return bound < rhs.bound || (bound == rhs.bound && (type == 0) > (rhs.type == 0)); // check comparator
    }
};

struct fenwick {
    int f[N];
    void update(int i) {
        ++i;
        while(i < N) ++f[i], i += i & -i;
    }
    int query(int i) {
        ++i;
        int ret = 0;
        while(i > 0) {
            ret += f[i];
            i -= i & -i;
        }
        return ret;
    }
};

class TaskE {

    int n, q, len, suff[N], res[N];
    string s[N];
    string concat;

    int sa[N], pos[N], tmp[N], lcp[N];
    void buildSA() {
        int gap;
        auto cmp = [&] (int i, int j) {
            if(pos[i] != pos[j]) return pos[i] < pos[j];
            i += gap;
            j += gap;
            return i < len && j < len ? pos[i] < pos[j] : i > j;
        };
        for(int i = 0; i < len; i++) {
            sa[i] = i;
            pos[i] = concat[i];
        }
        for(gap = 1; ; gap <<= 1) {
            sort(sa, sa + len, cmp);
            for(int i = 1; i < len; i++) tmp[i] = tmp[i-1] + cmp(sa[i-1], sa[i]);
            for(int i = 0; i < len; i++) pos[sa[i]] = tmp[i];
            if(tmp[len - 1] == len - 1) break;
        }
        for(int i = 0, k = 0; i < len; i++, k = (k ? k - 1 : 0)) {
            if(pos[i] == len - 1) {
                k = 0;
                continue;
            }
            int j = sa[pos[i] + 1];
            while(max(i + k, j + k) < len && concat[i + k] == concat[j + k]) ++k;
            lcp[pos[i]] = k;
        }
//        for(int i = 0; i < len; i++) cerr << i << ' ' << lcp[i] << ' ' << concat.substr(sa[i]) << endl;
    }

    int t[2*N];
    void build() {
        for(int i = 0; i < len; i++) t[len + i] = lcp[i];
        for(int i = len - 1; i >= 1; i--) t[i] = min(t[i << 1], t[i << 1 | 1]);
    }

    int query(int l, int r) {
        int ret = INF;
        for(l += len, r += len + 1; l < r; l >>= 1, r >>= 1) {
            if(l & 1) ret = min(t[l++], ret);
            if(r & 1) ret = min(t[--r], ret);
        }
        return ret;
    }

    int lb[N], rb[N];
    vector<node> ops;
    fenwick *fen;

public:
    void solve(istream &cin, ostream &cout) {
        cin >> n >> q;
        for(int i = 0; i < n; i++) {
            cin >> s[i];
            concat.push_back('?');
            suff[i] = concat.length();
            for(char c: s[i]) concat.push_back(c);
        }
        len = concat.length();
        buildSA();
        build();
        for(int i = 0; i < n; i++) {
            for(int j = suff[i]; j < suff[i] + s[i].length(); j++) {
                ops.emplace_back(i, 0, pos[j], pos[j], -1);
//                cerr << "insert " << i << ' ' << j << ' ' << pos[j] << endl;
            }
        }
        for(int i = 0; i < n; i++) {
            int l = 0, r = pos[suff[i]] - 1;
            while(l < r) {
                int mid = l+r >> 1;
                if(query(mid, pos[suff[i]] - 1) >= s[i].length()) r = mid;
                else l = mid + 1;
            }
            if(query(l, pos[suff[i]] - 1) >= s[i].length()) lb[i] = l;
            else lb[i] = pos[suff[i]];

            l = pos[suff[i]], r = len - 1;
            while(l < r) {
                int mid = l+r+1 >> 1;
                if(query(pos[suff[i]], mid) >= s[i].length()) l = mid;
                else r = mid - 1;
            }
            if(query(pos[suff[i]], l) >= s[i].length()) rb[i] = l + 1;
            else rb[i] = pos[suff[i]];
//            cerr << "precompute " << i << ' ' << lb[i] << ' ' << rb[i] << endl;
        }

        for(int i = 0, l, r, k; i < q; i++) {
            cin >> l >> r >> k;
            --l, --r, --k;
            ops.emplace_back(r, 1, lb[k], rb[k], i);
            if(l) ops.emplace_back(l - 1, -1, lb[k], rb[k], i);
        }
        sort(ops.begin(), ops.end());
        fen = new fenwick();
        for(node tt: ops) {
//            cerr << "nodes " << tt.bound << ' ' << tt.type << ' ' << tt.l << ' ' << tt.r << ' ' << tt.id << endl;
            if(tt.type == 0) {
                assert(tt.l == tt.r);
                fen->update(tt.l);
            } else {
                res[tt.id] += tt.type * (fen->query(tt.r) - fen->query(tt.l - 1));
            }
        }
        for(int i = 0; i < q; i++) cout << res[i] << '\n';
    }
};

class Solver {
public:
    void solve(std::istream& in, std::ostream& out) {
        TaskE *obj = new TaskE();
        obj->solve(in, out);
        delete obj;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    Solver solver;
    std::istream& in(std::cin);
    std::ostream& out(std::cout);
    solver.solve(in, out);
    return 0;
}


/*================================================================
*   Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
*   文件名称：E.cpp
*   创 建 者： badcw
*   创建日期： 10/22/19
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int root[maxn];
int cnt = 0;
struct node {
    int l, r, val;
}p[maxn * 40];

void update(int l, int r, int &now, int pos) {
    p[++cnt] = p[now];
    now = cnt;
    p[now].val ++;
    if (l == r) {
        return;
    }
    int mid = l + r >> 1;
    if (pos <= mid) update(l, mid, p[now].l, pos);
    else update(mid + 1, r, p[now].r, pos);
}

int le, re;

inline int query(int l, int r, int x, int y) {
    if (le <= l && r <= re) {
        return p[y].val - p[x].val;
    }
    int mid = l + r >> 1;
    int res = 0;
    if (le <= mid) res += query(l, mid, p[x].l, p[y].l);
    if (re > mid) res += query(mid + 1, r, p[x].r, p[y].r);
    return res;
}

int n;
string s[maxn];
vector<int> edge[maxn];
int pos[maxn];
int rnk[maxn], ot[maxn];
int tot;

class AC_automation
{
public:
    int trie[maxn][26];
    int tag[maxn];
    int fail[maxn], num[maxn], res[maxn], in[maxn];

    void init() {
        memset(trie, 0, sizeof trie);
        memset(tag, 0, sizeof tag);
        memset(fail, 0, sizeof fail);
    }

    int insert(string str) {
        int root = 0;
        for (int i = 0; i < str.length(); i++)
        {
            int id = str[i] - 'a';
            if (!trie[root][id]) {
                trie[root][id] = ++n;
            }
            root = trie[root][id];
        }
        return root;
    }

    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) que.push(trie[0][i]);
        while (!que.empty())
        {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++)
            {
                if (trie[k][i])
                {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]] ++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
} AC;

inline void build(int pre, int &now, int id) {
    int rt = 0;
    now = pre;
    for (char i : s[id]) {
        rt = AC.trie[rt][i - 'a'];
        update(1, n, now, rnk[rt]);
    }
}

inline void dfs(int u) {
    rnk[u] = ++tot;
    for (auto v : edge[u]) {
        dfs(v);
    }
    ot[u] = tot;
}

int N, Q;

int main(int argc, char* argv[]) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> s[i];
        pos[i] = AC.insert(s[i]);
    }
    AC.build();
    for (int i = 1; i <= n; ++i) {
        edge[AC.fail[i]].push_back(i);
    }
    dfs(0);
    for (int i = 1; i <= N; ++i) build(root[i - 1], root[i], i);
    while (Q--) {
        int l, r, k;
        cin >> l >> r >> k;
        le = rnk[pos[k]], re = ot[pos[k]];
        cout << query(1, n, root[l - 1], root[r]) << endl;
    }
    return 0;
}


/*================================================================
*   Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
*   文件名称：E.cpp
*   创 建 者： badcw
*   创建日期： 10/22/19
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int root[maxn];
int cnt = 0;
struct node {
    int l, r, val;
}p[maxn * 40];

void update(int l, int r, int pre, int &now, int pos) {
    if (pre == now) {
        p[now=++cnt] = p[pre];
    }
    p[now].val ++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (pos <= mid) update(l, mid, p[pre].l, p[now].l, pos);
    else update(mid + 1, r, p[pre].r, p[now].r, pos);
}

int le, re;

inline int query(int l, int r, int x, int y) {
    if (p[y].val == p[x].val) return 0;
    if (le <= l && r <= re) {
        return p[y].val - p[x].val;
    }
    int mid = l + r >> 1;
    int res = 0;
    if (le <= mid) res += query(l, mid, p[x].l, p[y].l);
    if (re > mid) res += query(mid + 1, r, p[x].r, p[y].r);
    return res;
}

int n;
string s[maxn];
vector<int> edge[maxn];
int pos[maxn];
int rnk[maxn], ot[maxn];
int tot;

class AC_automation
{
public:
    int trie[maxn][26];
    int tag[maxn];
    int fail[maxn], num[maxn], res[maxn], in[maxn];

    void init() {
        memset(trie, 0, sizeof trie);
        memset(tag, 0, sizeof tag);
        memset(fail, 0, sizeof fail);
    }

    int insert(string str) {
        int root = 0;
        for (int i = 0; i < str.length(); i++)
        {
            int id = str[i] - 'a';
            if (!trie[root][id]) {
                trie[root][id] = ++n;
            }
            root = trie[root][id];
        }
        return root;
    }

    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) que.push(trie[0][i]);
        while (!que.empty())
        {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++)
            {
                if (trie[k][i])
                {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]] ++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
} AC;

inline void build(int pre, int &now, int id) {
    int rt = 0;
    now = pre;
    for (char i : s[id]) {
        rt = AC.trie[rt][i - 'a'];
        update(1, n, pre, now, rnk[rt]);
    }
}

inline void dfs(int u) {
    rnk[u] = ++tot;
    for (auto v : edge[u]) {
        dfs(v);
    }
    ot[u] = tot;
}

int N, Q;

int main(int argc, char* argv[]) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> s[i];
        pos[i] = AC.insert(s[i]);
    }
//    n ++;
    AC.build();
    for (int i = 1; i <= n; ++i) {
        edge[AC.fail[i]].push_back(i);
//        cerr << "add edge: " << AC.fail[i] << " " << i << endl;
    }
    dfs(0);
    n++;
    for (int i = 1; i <= N; ++i) build(root[i - 1], root[i], i);
    while (Q--) {
        int l, r, k;
        cin >> l >> r >> k;
        le = rnk[pos[k]], re = ot[pos[k]];
        cout << query(1, n, root[l - 1], root[r]) << endl;
    }
    return 0;
}


/*================================================================
*   Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
*   文件名称：E.cpp
*   创 建 者： badcw
*   创建日期： 10/22/19
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int root[maxn];
int cnt = 0;
struct node {
    int l, r, val;
}p[maxn * 40];

void update(int l, int r, int pre, int &now, int pos) {
    if (pre == now) {
        p[now=++cnt] = p[pre];
    }
    p[now].val ++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (pos <= mid) update(l, mid, p[pre].l, p[now].l, pos);
    else update(mid + 1, r, p[pre].r, p[now].r, pos);
}

int le, re;

inline int query(int l, int r, int x, int y) {
    if (p[y].val == p[x].val) return 0;
    if (le <= l && r <= re) {
        return p[y].val - p[x].val;
    }
    int mid = l + r >> 1;
    int res = 0;
    if (le <= mid) res += query(l, mid, p[x].l, p[y].l);
    if (re > mid) res += query(mid + 1, r, p[x].r, p[y].r);
    return res;
}

int n;
string s[maxn];
vector<int> edge[maxn];
int pos[maxn];
int rnk[maxn], ot[maxn];
int tot;

class AC_automation
{
public:
    int trie[maxn][26];
    int tag[maxn];
    int fail[maxn], num[maxn], res[maxn], in[maxn];

    void init() {
        memset(trie, 0, sizeof trie);
        memset(tag, 0, sizeof tag);
        memset(fail, 0, sizeof fail);
    }

    int insert(string str) {
        int root = 0;
        for (int i = 0; i < str.length(); i++)
        {
            int id = str[i] - 'a';
            if (!trie[root][id]) {
                trie[root][id] = ++n;
            }
            root = trie[root][id];
        }
        return root;
    }

    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) que.push(trie[0][i]);
        while (!que.empty())
        {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++)
            {
                if (trie[k][i])
                {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]] ++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
} AC;

inline void build(int pre, int &now, int id) {
    int rt = 0;
    now = pre;
    for (char i : s[id]) {
        rt = AC.trie[rt][i - 'a'];
        update(1, n, pre, now, rnk[rt]);
    }
}

inline void dfs(int u) {
    rnk[u] = ++tot;
    for (auto v : edge[u]) {
        dfs(v);
    }
    ot[u] = tot;
}

int N, Q;

int main(int argc, char* argv[]) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> s[i];
        pos[i] = AC.insert(s[i]);
    }
    n ++;
    AC.build();
    for (int i = 1; i <= n; ++i) {
        edge[AC.fail[i]].push_back(i);
    }
    dfs(0);
    for (int i = 1; i <= N; ++i) build(root[i - 1], root[i], i);
    while (Q--) {
        int l, r, k;
        cin >> l >> r >> k;
        le = rnk[pos[k]], re = ot[pos[k]];
        cout << query(1, n, root[l - 1], root[r]) << endl;
    }
    return 0;
}


/*================================================================
*
*   创 建 者： badcw
*   创建日期： 2020/6/20 1:41
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5 + 50;
const int mod = 1e9 + 7;

ll qp(ll a, ll n, ll mod = ::mod) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

int now[maxn], out[maxn];
int tot;
class AC_automation {
public:
    int trie[maxn][26], cnt;
    int fail[maxn], in[maxn], Map[maxn], fa[maxn];
    void insert(char *str, int id) {
        int root = 0;
        for (int i = 0; str[i]; i++) {
            int id = str[i] - 'a';
            if (!trie[root][id]) trie[root][id] = ++cnt;
            fa[trie[root][id]] = root;
            root = trie[root][id];
        }
        Map[id] = root;
    }
    vector<int> edge[maxn];
    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) {
            edge[0].push_back(trie[0][i]);
//                cerr << "add edge: " << 0 << " " << trie[0][i] << endl;
            que.push(trie[0][i]);
        }
        while (!que.empty()) {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++) {
                if (trie[k][i]) {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    edge[fail[trie[k][i]]].push_back(trie[k][i]);
//                    cerr << "add edge: " << fail[trie[k][i]] << " " << trie[k][i] << endl;
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]]++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
    void dfs(int u) {
        now[u] = ++tot;
        for (auto v : edge[u]) {
            dfs(v);
        }
        out[u] = tot;
    }
} AC;

struct que {
    int x, k, sgn, pos;
    que(int i, int i1, int i2, int i3):x(i),k(i1),sgn(i2),pos(i3) {}

    bool operator < (const que& oth) const {
        return x < oth.x;
    }
};
int res[500005];
int N;
int pre[maxn];

void add(int pos) {
    while (pos <= N) {
        pre[pos] ++;
        pos += pos & -pos;
    }
}

int query(int pos) {
    int res = 0;
    while (pos > 0) {
        res += pre[pos];
        pos -= pos & -pos;
    }
    return res;
}

char s[maxn];
int main(int argc, char *argv[]) {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) {
        scanf("%s", s);
        AC.insert(s, i);
    }
    AC.build();
    AC.dfs(0);
    N = out[0];
    vector<que> ned;
    for (int i = 1; i <= q; ++i) {
        int x, y, k;
        scanf("%d%d%d", &x, &y, &k);
        ned.emplace_back(x - 1, k, -1, i);
        ned.emplace_back(y, k, 1, i);
    }
    sort(ned.begin(), ned.end());
    int pos = 1;
    for (auto i : ned) {
        while (pos <= i.x) {
//            cerr << "***add: " << pos << endl;
            int nowx = AC.Map[pos];
            while (nowx) {
//                cerr << now[nowx] << " ";
                add(now[nowx]), nowx = AC.fa[nowx];
            }
//            cerr << endl;
            pos ++;
        }
        res[i.pos] += i.sgn * (query(out[AC.Map[i.k]]) - query(now[AC.Map[i.k]] - 1));
    }
    for (int i = 1; i <= q; ++i) {
        printf("%d\n", res[i]);
    }
    return 0;
}


/*================================================================
*   Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
*   文件名称：E.cpp
*   创 建 者： badcw
*   创建日期： 10/22/19
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int root[maxn];
int cnt = 0;
struct node {
    int l, r, val;
}p[maxn * 40];

void update(int l, int r, int pre, int &now, int pos) {
    if (pre == now) {
        p[now=++cnt] = p[pre];
    }
    p[now].val ++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (pos <= mid) update(l, mid, p[pre].l, p[now].l, pos);
    else update(mid + 1, r, p[pre].r, p[now].r, pos);
}

int le, re;

inline int query(int l, int r, int x, int y) {
    if (p[y].val == p[x].val) return 0;
    if (le <= l && r <= re) {
        return p[y].val - p[x].val;
    }
    int mid = l + r >> 1;
    int res = 0;
    if (le <= mid) res += query(l, mid, p[x].l, p[y].l);
    if (re > mid) res += query(mid + 1, r, p[x].r, p[y].r);
    return res;
}

int n;
string s[maxn];
vector<int> edge[maxn];
int pos[maxn];
int rnk[maxn], ot[maxn];
int tot;

class AC_automation
{
public:
    int trie[maxn][26];
    int tag[maxn];
    int fail[maxn], num[maxn], res[maxn], in[maxn];

    void init() {
        memset(trie, 0, sizeof trie);
        memset(tag, 0, sizeof tag);
        memset(fail, 0, sizeof fail);
    }

    int insert(string str) {
        int root = 0;
        for (int i = 0; i < str.length(); i++)
        {
            int id = str[i] - 'a';
            if (!trie[root][id]) {
                trie[root][id] = ++n;
            }
            root = trie[root][id];
        }
        return root;
    }

    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) que.push(trie[0][i]);
        while (!que.empty())
        {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++)
            {
                if (trie[k][i])
                {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]] ++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
} AC;

inline void build(int pre, int &now, int id) {
    int rt = 0;
    now = pre;
    for (char i : s[id]) {
        rt = AC.trie[rt][i - 'a'];
        update(1, n, pre, now, rnk[rt]);
    }
}

inline void dfs(int u) {
    rnk[u] = ++tot;
    for (auto v : edge[u]) {
        dfs(v);
    }
    ot[u] = tot;
}

int N, Q;

int main(int argc, char* argv[]) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> s[i];
        pos[i] = AC.insert(s[i]);
    }
//    n ++;
    AC.build();
    for (int i = 1; i <= n; ++i) {
        edge[AC.fail[i]].push_back(i);
//        cerr << "add edge: " << AC.fail[i] << " " << i << endl;
    }
    dfs(0);
    for (int i = 1; i <= N; ++i) build(root[i - 1], root[i], i);
    while (Q--) {
        int l, r, k;
        cin >> l >> r >> k;
        le = rnk[pos[k]], re = ot[pos[k]];
        cout << query(1, n, root[l - 1], root[r]) << endl;
    }
    return 0;
}


/*================================================================
*   Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
*   文件名称：E.cpp
*   创 建 者： badcw
*   创建日期： 10/22/19
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int root[maxn];
int cnt = 0;
struct node {
    int l, r, val;
}p[maxn * 40];

void update(int l, int r, int pre, int &now, int pos) {
    if (pre == now) {
        now = ++cnt;
        p[now] = p[pre];
    }
    p[now].val ++;
    if (l == r) {
        return;
    }
    int mid = l + r >> 1;
    if (pos <= mid) update(l, mid, p[pre].l, p[now].l, pos);
    else update(mid + 1, r, p[pre].r, p[now].r, pos);
}

int le, re;

inline int query(int l, int r, int x, int y) {
    if (p[y].val == p[x].val) return 0;
    if (le <= l && r <= re) {
        return p[y].val - p[x].val;
    }
    int mid = l + r >> 1;
    int res = 0;
    if (le <= mid) res += query(l, mid, p[x].l, p[y].l);
    if (re > mid) res += query(mid + 1, r, p[x].r, p[y].r);
    return res;
}

int n;
string s[maxn];
vector<int> edge[maxn];
int pos[maxn];
int rnk[maxn], ot[maxn];
int tot;

class AC_automation
{
public:
    int trie[maxn][26];
    int tag[maxn];
    int fail[maxn], num[maxn], res[maxn], in[maxn];

    void init() {
        memset(trie, 0, sizeof trie);
        memset(tag, 0, sizeof tag);
        memset(fail, 0, sizeof fail);
    }

    int insert(string str) {
        int root = 0;
        for (int i = 0; i < str.length(); i++)
        {
            int id = str[i] - 'a';
            if (!trie[root][id]) {
                trie[root][id] = ++n;
            }
            root = trie[root][id];
        }
        return root;
    }

    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) que.push(trie[0][i]);
        while (!que.empty())
        {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++)
            {
                if (trie[k][i])
                {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]] ++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
} AC;

inline void build(int pre, int &now, int id) {
    int rt = 0;
    now = pre;
    for (char i : s[id]) {
        rt = AC.trie[rt][i - 'a'];
        update(1, n, pre, now, rnk[rt]);
    }
}

inline void dfs(int u) {
    rnk[u] = ++tot;
    for (auto v : edge[u]) {
        dfs(v);
    }
    ot[u] = tot;
}

int N, Q;

int main(int argc, char* argv[]) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> s[i];
        pos[i] = AC.insert(s[i]);
    }
    n ++;
    AC.build();
    for (int i = 1; i <= n; ++i) {
        edge[AC.fail[i]].push_back(i);
    }
    dfs(0);
    for (int i = 1; i <= N; ++i) build(root[i - 1], root[i], i);
    while (Q--) {
        int l, r, k;
        cin >> l >> r >> k;
        le = rnk[pos[k]], re = ot[pos[k]];
        cout << query(1, n, root[l - 1], root[r]) << endl;
    }
    return 0;
}


#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <numeric>
#include <functional>
#include <unordered_map>
#include <queue>
#include <set>
#include <random>
#include <map>

using namespace std;
typedef long long ll;

#define all(v) v.begin(), v.end()
#define szof(v) ((int)size(v))

namespace pbds{
    using namespace __gnu_pbds;

    template <class T, class cmp = std::less<T>>
    using set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;

    template <class key, class value, class cmp = std::less<key>>
    using map = tree<key, value, cmp, rb_tree_tag, tree_order_statistics_node_update>;
}

struct SuffixArray {
    string S;
    vector<int> Ind;

    SuffixArray(const string &s) : S(s) {
        int n = szof(s) + 1;
        Ind.resize(n);
        iota(all(Ind), 0);
        sort(all(Ind), [&](int a, int b) {
            return s[a] < s[b];
        });
        int colorsCnt = 1;
        vector<int> color(n), newColor(n), newInd(n);
        color[Ind[0]] = 0;
        for (int i = 1; i < n; ++i) {
            if (s[Ind[i]] != s[Ind[i - 1]]) {
                color[Ind[i]] = color[Ind[i - 1]] + 1;
                ++colorsCnt;
            } else
                color[Ind[i]] = color[Ind[i - 1]];
        }
        int logn = 1;
        while ((1 << logn) < n)
            ++logn;
        vector<int> sight(n);
        for (int lvl = 0; lvl < logn; ++lvl) {
            int len = 1 << lvl;
            for (int i = 0; i < n; ++i) {
                int j = i - len;
                if (j < 0)
                    j += n;
                sight[i] = j;
            }
            vector<int> cnt(colorsCnt, 0);
            for (auto x : color)
                ++cnt[x];
            for (int i = 1; i < colorsCnt - 1; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = colorsCnt - 1; i > 0; --i)
                cnt[i] = cnt[i - 1];
            cnt[0] = 0;
            for (int i = 0; i < n; ++i)
                newInd[cnt[color[sight[Ind[i]]]]++] = sight[Ind[i]];
            for (int i = 0; i < n; ++i) {
                int j = i + len;
                if (j >= n)
                    j -= n;
                sight[i] = j;
            }
            colorsCnt = 1;
            newColor[newInd[0]] = 0;
            for (int i = 1; i < n; ++i) {
                int j = newInd[i], k = newInd[i - 1];
                if (color[j] != color[k] || color[sight[j]] != color[sight[k]]) {
                    newColor[j] = newColor[k] + 1;
                    ++colorsCnt;
                } else
                    newColor[j] = newColor[k];
            }
            copy(all(newColor), color.begin());
            copy(all(newInd), Ind.begin());
        }
        Ind.erase(Ind.begin());
    }

    auto& GetArray() const {
        return Ind;
    }

    bool Check(const string& s, int j, bool flag) const {
        int cnt = min(szof(S) - j, szof(s)), i = 0;
        while (i < cnt && S[j + i] == s[i])
            ++i;
        if (flag)
            return i != cnt ? S[j + i] > s[i] : 0;
        return S[j + i] < s[i];
    }

    int SearchLeft(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 0))
                L = M;
            else
                R = M;
        }
        return R;
    }

    int SearchRight(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 1))
                R = M;
            else
                L = M;
        }
        return L;
    }

    pair<int, int> GetRange(const string& s) const {
        pair<int, int> res;
        res.first = SearchLeft(s);
        res.second = SearchRight(s);
        return res;
    }
};

struct SegmentTree {
    struct Node {
        int sum = 0;
        Node* left = nullptr;
        Node* right = nullptr;

        Node() = default;
    };

    Node* Init(int l, int r) {
        Node* vertex = new Node();
        if (l + 1 < r) {
            int m = (l + r) / 2;
            vertex->left = Init(l, m);
            vertex->right = Init(m, r);
        }
        return vertex;
    }

    Node* Add(Node* oldVertex, int l, int r, int point) {
        if (point < l || point >= r)
            return oldVertex;
        Node* newVertex = new Node();
        newVertex->sum = 1 + oldVertex->sum;
        if (l + 1 < r) {
            int m = (l + r) / 2;
            newVertex->left = Add(oldVertex->left, l, m, point);
            newVertex->right = Add(oldVertex->right, m, r, point);
        }
        return newVertex;
    }

    SegmentTree() = default;

    SegmentTree(const vector<int>& x) {
        int n = szof(x);
        root.resize(n + 1, nullptr);
        DiffNumsCnt = 1 + *max_element(all(x));
        root[n] = Init(0, DiffNumsCnt);
        for (int i = n - 1; i > -1; --i)
            root[i] = Add(root[i + 1], 0, DiffNumsCnt, x[i]);
    }

    int GetSum(Node* vertex, int l, int r, int L, int R) const {
        if (!vertex->sum || l >= R || L >= r)
            return 0;
        if (L <= l && r <= R)
            return vertex->sum;
        int m = (l + r) / 2;
        return GetSum(vertex->left, l, m, L, R) + GetSum(vertex->right, m, r, L, R);
    }

    int Query(int l, int r, int L, int R) const {
        return GetSum(root[l], 0, DiffNumsCnt, L, R + 1) - GetSum(root[r + 1], 0, DiffNumsCnt, L, R + 1);
    }

    int DiffNumsCnt;
    vector<Node*> root;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<string> s(n);
    for (int i = 0; i < n; ++i)
        cin >> s[i];
    string ss;
    for (int i = 0; i < n; ++i) {
        ss += s[i];
        if (i != n - 1)
            ss += '$';
    }
    int m = szof(ss);
    vector<int> stringIndex(m);
    for (int i = 0, j = 1; i < m; ++i) {
        if (ss[i] != '$')
            stringIndex[i] = j;
        else {
            ++j;
            stringIndex[i] = 0;
        }
    }

    auto GetStringIndexArray = [&] (const vector<int> &ind) {
        vector<int> res(all(ind));
        for (auto& val : res)
            val = stringIndex[val];
        return res;
    };

    const SuffixArray suffixArray(ss);
    const SegmentTree segmentTree(GetStringIndexArray(suffixArray.GetArray()));

    vector<bool> mark(n);
    vector<pair<int, int>> cache(n);

    int L, R, k;
    for (int i = 0; i < q; ++i) {
        cin >> L >> R >> k;
        pair<int, int> range;
        if (!mark[k - 1]) {
            cache[k - 1] = range = suffixArray.GetRange(s[k - 1]);
            mark[k - 1] = 1;
        } else
            range = cache[k - 1];
        cout << segmentTree.Query(range.first, range.second, L, R) << '\n';
    }

    return 0;
}


#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <numeric>
#include <functional>
#include <unordered_map>
#include <queue>
#include <set>
#include <random>
#include <map>

using namespace std;
typedef long long ll;

#define all(v) v.begin(), v.end()
#define szof(v) ((int)size(v))

namespace pbds{
    using namespace __gnu_pbds;

    template <class T, class cmp = std::less<T>>
    using set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;

    template <class key, class value, class cmp = std::less<key>>
    using map = tree<key, value, cmp, rb_tree_tag, tree_order_statistics_node_update>;
}

struct SuffixArray {
    string S;
    vector<int> Ind;

    SuffixArray(const string &s) : S(s) {
        int n = szof(s) + 1;
        Ind.resize(n);
        iota(all(Ind), 0);
        sort(all(Ind), [&](int a, int b) {
            return s[a] < s[b];
        });
        int colorsCnt = 1;
        vector<int> color(n), newColor(n), newInd(n);
        color[Ind[0]] = 0;
        for (int i = 1; i < n; ++i) {
            if (s[Ind[i]] != s[Ind[i - 1]]) {
                color[Ind[i]] = color[Ind[i - 1]] + 1;
                ++colorsCnt;
            } else
                color[Ind[i]] = color[Ind[i - 1]];
        }
        int logn = 1;
        while ((1 << logn) < n)
            ++logn;
        vector<int> sight(n);
        for (int lvl = 0; lvl < logn; ++lvl) {
            int len = 1 << lvl;
            for (int i = 0; i < n; ++i) {
                int j = i - len;
                if (j < 0)
                    j += n;
                sight[i] = j;
            }
            vector<int> cnt(colorsCnt, 0);
            for (auto x : color)
                ++cnt[x];
            for (int i = 1; i < colorsCnt - 1; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = colorsCnt - 1; i > 0; --i)
                cnt[i] = cnt[i - 1];
            cnt[0] = 0;
            for (int i = 0; i < n; ++i)
                newInd[cnt[color[sight[Ind[i]]]]++] = sight[Ind[i]];
            for (int i = 0; i < n; ++i) {
                int j = i + len;
                if (j >= n)
                    j -= n;
                sight[i] = j;
            }
            colorsCnt = 1;
            newColor[newInd[0]] = 0;
            for (int i = 1; i < n; ++i) {
                int j = newInd[i], k = newInd[i - 1];
                if (color[j] != color[k] || color[sight[j]] != color[sight[k]]) {
                    newColor[j] = newColor[k] + 1;
                    ++colorsCnt;
                } else
                    newColor[j] = newColor[k];
            }
            copy(all(newColor), color.begin());
            copy(all(newInd), Ind.begin());
        }
        Ind.erase(Ind.begin());
    }

    auto& GetArray() const {
        return Ind;
    }

    bool Check(const string& s, int j, bool flag) const {
        int cnt = min(szof(S) - j, szof(s)), i = 0;
        while (i < cnt && S[j + i] == s[i])
            ++i;
        if (flag)
            return i != cnt ? S[j + i] > s[i] : 0;
        return S[j + i] < s[i];
    }

    int SearchLeft(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 0))
                L = M;
            else
                R = M;
        }
        return R;
    }

    int SearchRight(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 1))
                R = M;
            else
                L = M;
        }
        return L;
    }

    pair<int, int> GetRange(const string& s) const {
        pair<int, int> res;
        res.first = SearchLeft(s);
        res.second = SearchRight(s);
        return res;
    }
};

struct SegmentTree {
    struct Node {
        int sum = 0;
        Node* left = nullptr;
        Node* right = nullptr;

        Node() = default;
    };

    Node* Init(int l, int r) {
        Node* vertex = new Node();
        if (l + 1 < r) {
            int m = (l + r) / 2;
            vertex->left = Init(l, m);
            vertex->right = Init(m, r);
        }
        return vertex;
    }

    Node* Add(Node* oldVertex, int l, int r, int point) {
        if (point < l || point >= r)
            return oldVertex;
        Node* newVertex = new Node();
        newVertex->sum = 1 + oldVertex->sum;
        if (l + 1 < r) {
            int m = (l + r) / 2;
            newVertex->left = Add(oldVertex->left, l, m, point);
            newVertex->right = Add(oldVertex->right, m, r, point);
        }
        return newVertex;
    }

    SegmentTree() = default;

    SegmentTree(const vector<int>& x) {
        int n = szof(x);
        root.resize(n + 1, nullptr);
        DiffNumsCnt = 1 + *max_element(all(x));
        root[n] = Init(0, DiffNumsCnt);
        for (int i = n - 1; i > -1; --i)
            root[i] = Add(root[i + 1], 0, DiffNumsCnt, x[i]);
    }

    int GetSum(Node* vertex, int l, int r, int L, int R) {
        if (!vertex->sum || l >= R || L >= r)
            return 0;
        if (L <= l && r <= R)
            return vertex->sum;
        int m = (l + r) / 2;
        return GetSum(vertex->left, l, m, L, R) + GetSum(vertex->right, m, r, L, R);
    }

    int Query(int l, int r, int L, int R) {
        return GetSum(root[l], 0, DiffNumsCnt, L, R + 1) - GetSum(root[r + 1], 0, DiffNumsCnt, L, R + 1);
    }

    int DiffNumsCnt;
    vector<Node*> root;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<string> s(n);
    for (int i = 0; i < n; ++i)
        cin >> s[i];
    string ss;
    for (int i = 0; i < n; ++i) {
        ss += s[i];
        if (i != n - 1)
            ss += '$';
    }
    int m = szof(ss);
    vector<int> stringIndex(m);
    for (int i = 0, j = 1; i < m; ++i) {
        if (ss[i] != '$')
            stringIndex[i] = j;
        else {
            ++j;
            stringIndex[i] = 0;
        }
    }

    auto GetStringIndexArray = [&] (const vector<int> &ind) {
        vector<int> res(all(ind));
        for (auto& val : res)
            val = stringIndex[val];
        return res;
    };

    SuffixArray suffixArray(ss);
    SegmentTree segmentTree(GetStringIndexArray(suffixArray.GetArray()));

    int L, R, k;
    for (int i = 0; i < q; ++i) {
        cin >> L >> R >> k;
        auto range = suffixArray.GetRange(s[k - 1]);
        cout << segmentTree.Query(range.first, range.second, L, R) << '\n';
    }

    return 0;
}


#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <numeric>
#include <functional>
#include <unordered_map>
#include <queue>
#include <set>
#include <random>
#include <map>

using namespace std;
typedef long long ll;

#define all(v) v.begin(), v.end()
#define szof(v) ((int)size(v))

namespace pbds{
    using namespace __gnu_pbds;

    template <class T, class cmp = std::less<T>>
    using set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;

    template <class key, class value, class cmp = std::less<key>>
    using map = tree<key, value, cmp, rb_tree_tag, tree_order_statistics_node_update>;
}

struct SuffixArray {
    string S;
    vector<int> Ind;

    SuffixArray(const string &s) : S(s) {
        int n = szof(s) + 1;
        Ind.resize(n);
        iota(all(Ind), 0);
        sort(all(Ind), [&](int a, int b) {
            return s[a] < s[b];
        });
        int colorsCnt = 1;
        vector<int> color(n), newColor(n), newInd(n);
        color[Ind[0]] = 0;
        for (int i = 1; i < n; ++i) {
            if (s[Ind[i]] != s[Ind[i - 1]]) {
                color[Ind[i]] = color[Ind[i - 1]] + 1;
                ++colorsCnt;
            } else
                color[Ind[i]] = color[Ind[i - 1]];
        }
        int logn = 1;
        while ((1 << logn) < n)
            ++logn;
        vector<int> sight(n);
        for (int lvl = 0; lvl < logn; ++lvl) {
            int len = 1 << lvl;
            for (int i = 0; i < n; ++i) {
                int j = i - len;
                if (j < 0)
                    j += n;
                sight[i] = j;
            }
            vector<int> cnt(colorsCnt, 0);
            for (auto x : color)
                ++cnt[x];
            for (int i = 1; i < colorsCnt - 1; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = colorsCnt - 1; i > 0; --i)
                cnt[i] = cnt[i - 1];
            cnt[0] = 0;
            for (int i = 0; i < n; ++i)
                newInd[cnt[color[sight[Ind[i]]]]++] = sight[Ind[i]];
            for (int i = 0; i < n; ++i) {
                int j = i + len;
                if (j >= n)
                    j -= n;
                sight[i] = j;
            }
            colorsCnt = 1;
            newColor[newInd[0]] = 0;
            for (int i = 1; i < n; ++i) {
                int j = newInd[i], k = newInd[i - 1];
                if (color[j] != color[k] || color[sight[j]] != color[sight[k]]) {
                    newColor[j] = newColor[k] + 1;
                    ++colorsCnt;
                } else
                    newColor[j] = newColor[k];
            }
            copy(all(newColor), color.begin());
            copy(all(newInd), Ind.begin());
        }
        Ind.erase(Ind.begin());
    }

    auto& GetArray() const {
        return Ind;
    }

    bool Check(const string& s, int j, bool flag) const {
        int cnt = min(szof(S) - j, szof(s)), i = 0;
        while (i < cnt && S[j + i] == s[i])
            ++i;
        if (flag)
            return i != cnt ? S[j + i] > s[i] : 0;
        return S[j + i] < s[i];
    }

    int SearchLeft(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 0))
                L = M;
            else
                R = M;
        }
        return R;
    }

    int SearchRight(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 1))
                R = M;
            else
                L = M;
        }
        return L;
    }

    pair<int, int> GetRange(const string& s) const {
        pair<int, int> res;
        res.first = SearchLeft(s);
        res.second = SearchRight(s);
        return res;
    }
};

struct SegmentTree {

    SegmentTree() = default;

    SegmentTree(const vector<int>& x) {
        int n = szof(x);
        TreeSize = 1;
        while (TreeSize < n)
            TreeSize *= 2;
        tree.resize(2 * TreeSize);
        for (int i = 0; i < n; ++i)
            tree[i + TreeSize] = { x[i] };
        for (int i = TreeSize - 1; i; --i)
            merge(all(tree[2 * i]), all(tree[2 * i + 1]), back_inserter(tree[i]));
    }

    int Query(int l, int r, int L, int R) const {
        int ans = 0;
        l += TreeSize;
        r += TreeSize;
        while (l <= r) {
            if (l & 1) {
                ans += upper_bound(all(tree[l]), R) - lower_bound(all(tree[l]), L);
                ++l;
            }
            if (!(r & 1)) {
                ans += upper_bound(all(tree[r]), R) - lower_bound(all(tree[r]), L);
                --r;
            }
            l /= 2;
            r /= 2;
        }
        return ans;
    }

    int TreeSize;
    vector<vector<int>> tree;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<string> s(n);
    for (int i = 0; i < n; ++i)
        cin >> s[i];
    string ss;
    for (int i = 0; i < n; ++i) {
        ss += s[i];
        if (i != n - 1)
            ss += '$';
    }
    int m = szof(ss);
    vector<int> stringIndex(m);
    for (int i = 0, j = 1; i < m; ++i) {
        if (ss[i] != '$')
            stringIndex[i] = j;
        else {
            ++j;
            stringIndex[i] = 0;
        }
    }

    auto GetStringIndexArray = [&] (const vector<int> &ind) {
        vector<int> res(all(ind));
        for (auto& val : res)
            val = stringIndex[val];
        return res;
    };

    const SuffixArray suffixArray(ss);
    const SegmentTree segmentTree(GetStringIndexArray(suffixArray.GetArray()));

    vector<bool> mark(n);
    vector<pair<int, int>> cache(n);

    int L, R, k;
    for (int i = 0; i < q; ++i) {
        cin >> L >> R >> k;
        pair<int, int> range;
        if (!mark[k - 1]) {
            cache[k - 1] = range = suffixArray.GetRange(s[k - 1]);
            mark[k - 1] = 1;
        } else
            range = cache[k - 1];
        cout << segmentTree.Query(range.first, range.second, L, R) << '\n';
    }

    return 0;
}


#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <numeric>
#include <functional>
#include <unordered_map>
#include <queue>
#include <set>
#include <random>
#include <map>

using namespace std;
typedef long long ll;

#define all(v) v.begin(), v.end()
#define szof(v) ((int)size(v))

namespace pbds{
    using namespace __gnu_pbds;

    template <class T, class cmp = std::less<T>>
    using set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;

    template <class key, class value, class cmp = std::less<key>>
    using map = tree<key, value, cmp, rb_tree_tag, tree_order_statistics_node_update>;
}

struct SuffixArray {
    string S;
    vector<int> Ind;

    SuffixArray(const string &s) : S(s) {
        int n = szof(s) + 1;
        Ind.resize(n);
        iota(all(Ind), 0);
        sort(all(Ind), [&](int a, int b) {
            return s[a] < s[b];
        });
        int colorsCnt = 1;
        vector<int> color(n), newColor(n), newInd(n);
        color[Ind[0]] = 0;
        for (int i = 1; i < n; ++i) {
            if (s[Ind[i]] != s[Ind[i - 1]]) {
                color[Ind[i]] = color[Ind[i - 1]] + 1;
                ++colorsCnt;
            } else
                color[Ind[i]] = color[Ind[i - 1]];
        }
        int logn = 1;
        while ((1 << logn) < n)
            ++logn;
        vector<int> sight(n);
        for (int lvl = 0; lvl < logn; ++lvl) {
            int len = 1 << lvl;
            for (int i = 0; i < n; ++i) {
                int j = i - len;
                if (j < 0)
                    j += n;
                sight[i] = j;
            }
            vector<int> cnt(colorsCnt, 0);
            for (auto x : color)
                ++cnt[x];
            for (int i = 1; i < colorsCnt - 1; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = colorsCnt - 1; i > 0; --i)
                cnt[i] = cnt[i - 1];
            cnt[0] = 0;
            for (int i = 0; i < n; ++i)
                newInd[cnt[color[sight[Ind[i]]]]++] = sight[Ind[i]];
            for (int i = 0; i < n; ++i) {
                int j = i + len;
                if (j >= n)
                    j -= n;
                sight[i] = j;
            }
            colorsCnt = 1;
            newColor[newInd[0]] = 0;
            for (int i = 1; i < n; ++i) {
                int j = newInd[i], k = newInd[i - 1];
                if (color[j] != color[k] || color[sight[j]] != color[sight[k]]) {
                    newColor[j] = newColor[k] + 1;
                    ++colorsCnt;
                } else
                    newColor[j] = newColor[k];
            }
            copy(all(newColor), color.begin());
            copy(all(newInd), Ind.begin());
        }
        Ind.erase(Ind.begin());
    }

    auto& GetArray() const {
        return Ind;
    }

    bool Check(const string& s, int j, bool flag) const {
        int cnt = min(szof(S) - j, szof(s)), i = 0;
        while (i < cnt && S[j + i] == s[i])
            ++i;
        if (flag)
            return i != cnt ? S[j + i] > s[i] : 0;
        return S[j + i] < s[i];
    }

    int SearchLeft(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 0))
                L = M;
            else
                R = M;
        }
        return R;
    }

    int SearchRight(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 1))
                R = M;
            else
                L = M;
        }
        return L;
    }

    pair<int, int> GetRange(const string& s) const {
        pair<int, int> res;
        res.first = SearchLeft(s);
        res.second = SearchRight(s);
        return res;
    }
};

struct SegmentTree {
    struct Node {
        int sum = 0;
        Node* left = nullptr;
        Node* right = nullptr;

        Node() = default;
    };

    Node* Init(int l, int r) {
        Node* vertex = new Node();
        if (l + 1 < r) {
            int m = (l + r) / 2;
            vertex->left = Init(l, m);
            vertex->right = Init(m, r);
        }
        return vertex;
    }

    Node* Add(Node* oldVertex, int l, int r, int point) {
        if (point < l || point >= r)
            return oldVertex;
        Node* newVertex = new Node();
        newVertex->sum = 1 + oldVertex->sum;
        if (l + 1 < r) {
            int m = (l + r) / 2;
            newVertex->left = Add(oldVertex->left, l, m, point);
            newVertex->right = Add(oldVertex->right, m, r, point);
        }
        return newVertex;
    }

    SegmentTree() = default;

    SegmentTree(const vector<int>& x) {
        int n = szof(x);
        root.resize(n + 1, nullptr);
        DiffNumsCnt = 1 + *max_element(all(x));
        root[n] = Init(0, DiffNumsCnt);
        for (int i = n - 1; i > -1; --i)
            root[i] = Add(root[i + 1], 0, DiffNumsCnt, x[i]);
    }

    int GetSum(Node* vertex, int l, int r, int L, int R) {
        if (!vertex->sum || l >= R || L >= r)
            return 0;
        if (L <= l && r <= R)
            return vertex->sum;
        int m = (l + r) / 2;
        return GetSum(vertex->left, l, m, L, R) + GetSum(vertex->right, m, r, L, R);
    }

    int Query(int l, int r, int L, int R) {
        return GetSum(root[l], 0, DiffNumsCnt, L, R + 1) - GetSum(root[r + 1], 0, DiffNumsCnt, L, R + 1);
    }

    int DiffNumsCnt;
    vector<Node*> root;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<string> s(n);
    for (int i = 0; i < n; ++i)
        cin >> s[i];
    string ss;
    for (int i = 0; i < n; ++i) {
        ss += s[i];
        if (i != n - 1)
            ss += '$';
    }
    int m = szof(ss);
    vector<int> stringIndex(m);
    for (int i = 0, j = 1; i < m; ++i) {
        if (ss[i] != '$')
            stringIndex[i] = j;
        else {
            ++j;
            stringIndex[i] = 0;
        }
    }

    auto GetStringIndexArray = [&] (const vector<int> &ind) {
        vector<int> res(all(ind));
        for (auto& val : res)
            val = stringIndex[val];
        return res;
    };

    SuffixArray suffixArray(ss);
    SegmentTree segmentTree(GetStringIndexArray(suffixArray.GetArray()));

    vector<bool> mark(n);
    vector<pair<int, int>> cache(n);

    int L, R, k;
    for (int i = 0; i < q; ++i) {
        cin >> L >> R >> k;
        pair<int, int> range;
        if (!mark[k - 1]) {
            cache[k - 1] = range = suffixArray.GetRange(s[k - 1]);
            mark[k - 1] = 1;
        } else
            range = cache[k - 1];
        cout << segmentTree.Query(range.first, range.second, L, R) << '\n';
    }

    return 0;
}


#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <numeric>
#include <functional>
#include <unordered_map>
#include <queue>
#include <set>
#include <random>
#include <map>

using namespace std;
typedef long long ll;

#define all(v) v.begin(), v.end()
#define szof(v) ((int)size(v))

namespace pbds{
    using namespace __gnu_pbds;

    template <class T, class cmp = std::less<T>>
    using set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;

    template <class key, class value, class cmp = std::less<key>>
    using map = tree<key, value, cmp, rb_tree_tag, tree_order_statistics_node_update>;
}

struct SuffixArray {
    string S;
    vector<int> Ind;

    SuffixArray(const string &s) : S(s) {
        int n = szof(s) + 1;
        Ind.resize(n);
        iota(all(Ind), 0);
        sort(all(Ind), [&](int a, int b) {
            return s[a] < s[b];
        });
        int colorsCnt = 1;
        vector<int> color(n), newColor(n), newInd(n);
        color[Ind[0]] = 0;
        for (int i = 1; i < n; ++i) {
            if (s[Ind[i]] != s[Ind[i - 1]]) {
                color[Ind[i]] = color[Ind[i - 1]] + 1;
                ++colorsCnt;
            } else
                color[Ind[i]] = color[Ind[i - 1]];
        }
        int logn = 1;
        while ((1 << logn) < n)
            ++logn;
        vector<int> sight(n);
        for (int lvl = 0; lvl < logn; ++lvl) {
            int len = 1 << lvl;
            for (int i = 0; i < n; ++i) {
                int j = i - len;
                if (j < 0)
                    j += n;
                sight[i] = j;
            }
            vector<int> cnt(colorsCnt, 0);
            for (auto x : color)
                ++cnt[x];
            for (int i = 1; i < colorsCnt - 1; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = colorsCnt - 1; i > 0; --i)
                cnt[i] = cnt[i - 1];
            cnt[0] = 0;
            for (int i = 0; i < n; ++i)
                newInd[cnt[color[sight[Ind[i]]]]++] = sight[Ind[i]];
            for (int i = 0; i < n; ++i) {
                int j = i + len;
                if (j >= n)
                    j -= n;
                sight[i] = j;
            }
            colorsCnt = 1;
            newColor[newInd[0]] = 0;
            for (int i = 1; i < n; ++i) {
                int j = newInd[i], k = newInd[i - 1];
                if (color[j] != color[k] || color[sight[j]] != color[sight[k]]) {
                    newColor[j] = newColor[k] + 1;
                    ++colorsCnt;
                } else
                    newColor[j] = newColor[k];
            }
            copy(all(newColor), color.begin());
            copy(all(newInd), Ind.begin());
        }
        Ind.erase(Ind.begin());
    }

    auto& GetArray() const {
        return Ind;
    }

    bool Check(const string& s, int j, bool flag) const {
        int cnt = min(szof(S) - j, szof(s)), i = 0;
        while (i < cnt && S[j + i] == s[i])
            ++i;
        if (flag)
            return i != cnt ? S[j + i] > s[i] : 0;
        return S[j + i] < s[i];
    }

    int SearchLeft(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 0))
                L = M;
            else
                R = M;
        }
        return R;
    }

    int SearchRight(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 1))
                R = M;
            else
                L = M;
        }
        return L;
    }

    pair<int, int> GetRange(const string& s) const {
        pair<int, int> res;
        res.first = SearchLeft(s);
        res.second = SearchRight(s);
        return res;
    }
};

struct SegmentTree {
    struct Node {
        int sum = 0, mark = 0;
        Node* left = nullptr;
        Node* right = nullptr;

        Node() = default;
    };

    Node* Init(int l, int r) {
        Node* vertex = new Node();
        if (l + 1 < r) {
            int m = (l + r) / 2;
            vertex->left = Init(l, m);
            vertex->right = Init(m, r);
        }
        return vertex;
    }

    Node* Add(Node* oldVertex, int l, int r, int point) {
        if (point < l || point >= r)
            return oldVertex;
        Node* newVertex = new Node();
        newVertex->sum = 1 + oldVertex->sum;
        if (l + 1 < r) {
            int m = (l + r) / 2;
            newVertex->left = Add(oldVertex->left, l, m, point);
            newVertex->right = Add(oldVertex->right, m, r, point);
        }
        return newVertex;
    }

    SegmentTree() = default;

    SegmentTree(const vector<int>& x) {
        int n = szof(x);
        root.resize(n + 1, nullptr);
        DiffNumsCnt = 1 + *max_element(all(x));
        root[n] = Init(0, DiffNumsCnt);
        for (int i = n - 1; i > -1; --i)
            root[i] = Add(root[i + 1], 0, DiffNumsCnt, x[i]);
    }

    int GetSum(Node* vertex, int l, int r, int L, int R) const {
        if (!vertex->sum || l >= R || L >= r)
            return 0;
        if (L <= l && r <= R)
            return vertex->sum;
        int m = (l + r) / 2;
        return GetSum(vertex->left, l, m, L, R) + GetSum(vertex->right, m, r, L, R);
    }

    int Query(int l, int r, int L, int R) const {
        return GetSum(root[l], 0, DiffNumsCnt, L, R + 1) - GetSum(root[r + 1], 0, DiffNumsCnt, L, R + 1);
    }

    void dfs(Node* v) {
        if (!v || v->mark)
            return;
        dfs(v->left);
        dfs(v->right);
        v->mark = 1;
        TopSort.push_back(v);
    }

    ~SegmentTree() {
        for (int i = szof(root) - 1; i > -1; --i)
            dfs(root[i]);
        for (auto ptr : TopSort)
            free(ptr);
    }

    int DiffNumsCnt;
    vector<Node*> TopSort;
    vector<Node*> root;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<string> s(n);
    for (int i = 0; i < n; ++i)
        cin >> s[i];
    string ss;
    for (int i = 0; i < n; ++i) {
        ss += s[i];
        if (i != n - 1)
            ss += '$';
    }
    int m = szof(ss);
    vector<int> stringIndex(m);
    for (int i = 0, j = 1; i < m; ++i) {
        if (ss[i] != '$')
            stringIndex[i] = j;
        else {
            ++j;
            stringIndex[i] = 0;
        }
    }

    auto GetStringIndexArray = [&] (const vector<int> &ind) {
        vector<int> res(all(ind));
        for (auto& val : res)
            val = stringIndex[val];
        return res;
    };

    const SuffixArray suffixArray(ss);
    const SegmentTree segmentTree(GetStringIndexArray(suffixArray.GetArray()));

    vector<bool> mark(n);
    vector<pair<int, int>> cache(n);

    int L, R, k;
    for (int i = 0; i < q; ++i) {
        cin >> L >> R >> k;
        pair<int, int> range;
        if (!mark[k - 1]) {
            cache[k - 1] = range = suffixArray.GetRange(s[k - 1]);
            mark[k - 1] = 1;
        } else
            range = cache[k - 1];
        cout << segmentTree.Query(range.first, range.second, L, R) << '\n';
    }

    return 0;
}


#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <numeric>
#include <functional>
#include <unordered_map>
#include <queue>
#include <set>
#include <random>
#include <map>

using namespace std;
typedef long long ll;

#define all(v) v.begin(), v.end()
#define szof(v) ((int)size(v))

namespace pbds{
    using namespace __gnu_pbds;

    template <class T, class cmp = std::less<T>>
    using set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;

    template <class key, class value, class cmp = std::less<key>>
    using map = tree<key, value, cmp, rb_tree_tag, tree_order_statistics_node_update>;
}

struct SuffixArray {
    string S;
    vector<int> Ind;

    SuffixArray(const string &s) : S(s) {
        int n = szof(s) + 1;
        Ind.resize(n);
        iota(all(Ind), 0);
        sort(all(Ind), [&](int a, int b) {
            return s[a] < s[b];
        });
        int colorsCnt = 1;
        vector<int> color(n), newColor(n), newInd(n);
        color[Ind[0]] = 0;
        for (int i = 1; i < n; ++i) {
            if (s[Ind[i]] != s[Ind[i - 1]]) {
                color[Ind[i]] = color[Ind[i - 1]] + 1;
                ++colorsCnt;
            } else
                color[Ind[i]] = color[Ind[i - 1]];
        }
        int logn = 1;
        while ((1 << logn) < n)
            ++logn;
        vector<int> sight(n);
        for (int lvl = 0; lvl < logn; ++lvl) {
            int len = 1 << lvl;
            for (int i = 0; i < n; ++i) {
                int j = i - len;
                if (j < 0)
                    j += n;
                sight[i] = j;
            }
            vector<int> cnt(colorsCnt, 0);
            for (auto x : color)
                ++cnt[x];
            for (int i = 1; i < colorsCnt - 1; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = colorsCnt - 1; i > 0; --i)
                cnt[i] = cnt[i - 1];
            cnt[0] = 0;
            for (int i = 0; i < n; ++i)
                newInd[cnt[color[sight[Ind[i]]]]++] = sight[Ind[i]];
            for (int i = 0; i < n; ++i) {
                int j = i + len;
                if (j >= n)
                    j -= n;
                sight[i] = j;
            }
            colorsCnt = 1;
            newColor[newInd[0]] = 0;
            for (int i = 1; i < n; ++i) {
                int j = newInd[i], k = newInd[i - 1];
                if (color[j] != color[k] || color[sight[j]] != color[sight[k]]) {
                    newColor[j] = newColor[k] + 1;
                    ++colorsCnt;
                } else
                    newColor[j] = newColor[k];
            }
            copy(all(newColor), color.begin());
            copy(all(newInd), Ind.begin());
        }
        Ind.erase(Ind.begin());
    }

    auto& GetArray() const {
        return Ind;
    }

    bool Check(const string& s, int j, bool flag) const {
        int cnt = min(szof(S) - j, szof(s)), i = 0;
        while (i < cnt && S[j + i] == s[i])
            ++i;
        if (flag)
            return i != cnt ? S[j + i] > s[i] : 0;
        return S[j + i] < s[i];
    }

    int SearchLeft(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 0))
                L = M;
            else
                R = M;
        }
        return R;
    }

    int SearchRight(const string& s) const {
        int L = -1, R = szof(Ind);
        while (R - L > 1) {
            int M = (L + R) / 2;
            if (Check(s, Ind[M], 1))
                R = M;
            else
                L = M;
        }
        return L;
    }

    pair<int, int> GetRange(const string& s) const {
        pair<int, int> res;
        res.first = SearchLeft(s);
        res.second = SearchRight(s);
        return res;
    }
};

struct SegmentTree {
    struct Node {
        int sum = 0;
        Node* left = nullptr;
        Node* right = nullptr;

        Node() = default;
    };

    Node* Init(int l, int r) {
        Node* vertex = new Node();
        if (l + 1 < r) {
            int m = (l + r) / 2;
            vertex->left = Init(l, m);
            vertex->right = Init(m, r);
        }
        return vertex;
    }

    Node* Add(Node* oldVertex, int l, int r, int point) {
        if (point < l || point >= r)
            return oldVertex;
        Node* newVertex = new Node();
        newVertex->sum = 1 + oldVertex->sum;
        if (l + 1 < r) {
            int m = (l + r) / 2;
            newVertex->left = Add(oldVertex->left, l, m, point);
            newVertex->right = Add(oldVertex->right, m, r, point);
        }
        return newVertex;
    }

    SegmentTree() = default;

    SegmentTree(const vector<int>& x) {
        int n = szof(x);
        root.resize(n + 1, nullptr);
        DiffNumsCnt = 1 + *max_element(all(x));
        root[n] = Init(0, DiffNumsCnt);
        for (int i = n - 1; i > -1; --i)
            root[i] = Add(root[i + 1], 0, DiffNumsCnt, x[i]);
    }

    int GetSum(Node* vertex, int l, int r, int L, int R) {
        if (!vertex->sum || l >= R || L >= r)
            return 0;
        if (L <= l && r <= R)
            return vertex->sum;
        int m = (l + r) / 2;
        return GetSum(vertex->left, l, m, L, R) + GetSum(vertex->right, m, r, L, R);
    }

    int Query(int l, int r, int L, int R) {
        return GetSum(root[l], 0, DiffNumsCnt, L, R + 1) - GetSum(root[r + 1], 0, DiffNumsCnt, L, R + 1);
    }

    int DiffNumsCnt;
    vector<Node*> root;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<string> s(n);
    for (int i = 0; i < n; ++i)
        cin >> s[i];
    string ss;
    for (int i = 0; i < n; ++i) {
        ss += s[i];
        if (i != n - 1)
            ss += '$';
    }
    int m = szof(ss);
    vector<int> stringIndex(m);
    for (int i = 0, j = 1; i < m; ++i) {
        if (ss[i] != '$')
            stringIndex[i] = j;
        else {
            ++j;
            stringIndex[i] = 0;
        }
    }

    auto GetStringIndexArray = [&] (const vector<int> &ind) {
        vector<int> res(all(ind));
        for (auto& val : res)
            val = stringIndex[val];
        return res;
    };

    const SuffixArray suffixArray(ss);
    const SegmentTree segmentTree(GetStringIndexArray(suffixArray.GetArray()));

    vector<bool> mark(n);
    vector<pair<int, int>> cache(n);

    int L, R, k;
    for (int i = 0; i < q; ++i) {
        cin >> L >> R >> k;
        pair<int, int> range;
        if (!mark[k - 1]) {
            cache[k - 1] = range = suffixArray.GetRange(s[k - 1]);
            mark[k - 1] = 1;
        } else
            range = cache[k - 1];
        cout << segmentTree.Query(range.first, range.second, L, R) << '\n';
    }

    return 0;
}


#pragma GCC optimize("O2")
//#pragma GCC optimize("unroll-loops")
//#pragma GCC target("avx,avx2,sse,sse2,ssse3,tune=native")
#include<bits/stdc++.h>
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define pb push_back
using namespace std;
using ll = long long;
//using arr = array;
using pi = pair<int, int>;
using vi = vector<int>;
using vpi = vector<pi>;
using ld = long double;
//#include<bits/extc++.h>
//using namespace __gnu_pbds;
//using oset = tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update>;
const int maxn = 1<<18, mod = (119<<23)+1, lg = 18;
struct node {
	int sum = 0;
	node *l, *r;
	node(int x) {sum = x, l = r = 0;}
};
using pnode = node*;
deque<node> buffer;
pnode newnode(int x) {
	buffer.emplace_back(x);
	return &buffer.back();
}
pnode merge(pnode a, pnode b) {
	pnode t = newnode(a->sum + b->sum);
	t->l = a, t->r = b;
	return t;
}
pnode build(int l, int r) {
	if(l == r) return newnode(0);
	int mid = (l+r)>>1;
	return merge(build(l, mid), build(mid+1, r));
}
pnode upd(int l, int r, int i, int x, pnode rt) {
	if(i < l || r < i) return rt;
	if(l == r) return newnode(rt->sum + x);
	int mid = (l+r)>>1;
	return merge(upd(l, mid, i, x, rt->l), upd(mid+1, r, i, x, rt->r));
}
int get(int l, int r, int ql, int qr, pnode rt) {
	if(ql > r || qr < l) return 0;
	if(ql <= l && r <= qr) return rt->sum;
	int mid = (l+r)>>1;
	return get(l, mid, ql, qr, rt->l) + get(mid+1, r, ql, qr, rt->r);
}

vi g[maxn];
int tin[maxn], tout[maxn], timer = 0;
void dfs(int v) {
	tin[v] = timer++;
	for(auto i : g[v]) {
		dfs(i);
	}
	tout[v] = timer-1;
}
struct aho {
	int to[maxn][26], link[maxn], sz;
	void init() {
		sz = 1;
		memset(to, 0, sizeof to);
		memset(link, 0, sizeof link);
	}
	int add(string &s) {
		int cur = 0;
		for(auto i : s) {
			i -= 'a';
			if(!to[cur][i]) {to[cur][i] = sz++;}
			cur = to[cur][i];
		}
		return cur;
	}
	void push_links() {
		int q[maxn], st = 0, fi = 1;
		q[0] = 0;
		while(st < fi) {
			int u = q[st++];
			int v = link[u];
			for(int i = 0; i < 26; i++) {
				if(to[u][i]) {
					q[fi++] = to[u][i];
					link[to[u][i]] = u ? to[v][i] : 0;
				} else {
					to[u][i] = to[v][i];
				}
			}
		}
		for(int i = 1; i < sz; i++)
			g[link[i]].pb(i);
		dfs(0);
	}
};
int n, m, term[maxn];
aho a;
string s[maxn];
string bigs;
pnode rt[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	a.init();
	for(int i = 1; i <= n; i++) cin >> s[i], term[i] = a.add(s[i]);
	rt[0] = build(1, a.sz);
	a.push_links();
	for(int i = 1; i <= n; i++) {
		rt[i] = rt[i-1];
		int cur = 0;
		for(auto c : s[i]) {
			c -= 'a';
			cur = a.to[cur][c];
			rt[i] = upd(1, a.sz, tin[cur], 1, rt[i]);
		}
	}
	int l, r, x;
	while(m--) {
		cin >> l >> r >> x;
		cout << get(1, a.sz, tin[term[x]], tout[term[x]], rt[r]) - get(1, a.sz, tin[term[x]], tout[term[x]], rt[l-1]) << '\n';
	}
}


#include <bits/stdc++.h>
using namespace std;

const int D = 26;

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	int N, Q; cin >> N >> Q;

	vector<array<int, D>> trans(1); trans[0].fill(0);
	vector<int> par(1, -1);

	vector<int> loc(N);
	for (int i = 0; i < N; i++) {
		string S; cin >> S;
		int cur = 0;
		for (char c : S) {
			if (!trans[cur][c-'a']) {
				int nxt = int(trans.size());
				trans.push_back(array<int, D>()); trans.back().fill(0);
				par.push_back(cur);

				trans[cur][c-'a'] = nxt;
			}
			cur = trans[cur][c-'a'];
		}
		loc[i] = cur;
	}

	vector<vector<int>> failureAdj(trans.size());
	vector<int> failure(trans.size(), -1);
	{
		vector<int> q; q.reserve(trans.size());
		q.push_back(0);
		for (int i = 0; i < int(q.size()); i++) {
			int cur = q[i];
			for (int nxt : trans[cur]) {
				if (!nxt) continue;
				q.push_back(nxt);
			}
		}
		assert(q.size() == trans.size());

		for (int cur : q) {
			if (cur) {
				failureAdj[failure[cur]].push_back(cur);
			}
			for (int c = 0; c < D; c++) {
				(trans[cur][c] ? failure[trans[cur][c]] : trans[cur][c]) = (cur ? trans[failure[cur]][c] : 0);
			}
		}
	}

	vector<int> failureSt(trans.size());
	vector<int> failureEn(trans.size());
	{
		int idx = 0;
		function<void(int)> dfs = [&](int cur) -> void {
			failureSt[cur] = idx++;
			for (int nxt : failureAdj[cur]) {
				dfs(nxt);
			}
			failureEn[cur] = idx;
		};
		dfs(0);
		assert(idx == int(trans.size()));
	}

	vector<vector<pair<int, int>>> queries(N);
	for (int q = 0; q < Q; q++) {
		int l, r, k; cin >> l >> r >> k; l -= 2, r -= 1, k--;
		if (l >= 0) {
			queries[l].emplace_back(loc[k], ~q);
		}
		queries[r].emplace_back(loc[k], q);
	}

	vector<int> ans(Q);
	const int S = 1 << 18;
	vector<int> seg(2*S);
	for (int i = 0; i < N; i++) {
		for (int v = loc[i]; v != -1; v = par[v]) {
			for (int a = failureSt[v]+S; a; a /= 2) {
				seg[a]++;
			}
		}

		for (auto it : queries[i]) {
			int v = it.first;
			int q = it.second;
			int s = 0;
			for (int a = failureSt[v]+S, b = failureEn[v]+S; a < b; a /= 2, b /= 2) {
				if (a & 1) s += seg[a++];
				if (b & 1) s += seg[--b];
			}
			if (q >= 0) {
				ans[q] += s;
			} else {
				ans[~q] -= s;
			}
		}
	}

	for (int v : ans) { cout << v << '\n'; }

	return 0;
}


#include<bits/stdc++.h>
using namespace std;

typedef pair<int, int> ii;

#define MAX_N 500010
char T[MAX_N];
int n;
int RA[MAX_N], tempRA[MAX_N];
int SA[MAX_N], tempSA[MAX_N];
int c[MAX_N];

char P[MAX_N];
int m;

void countingSort(int k) {
  int i, sum, maxi = max(300, n);
  memset(c, 0, sizeof c);
  for (i = 0; i < n; i++)
    c[i + k < n ? RA[i + k] : 0]++;
  for (i = sum = 0; i < maxi; i++) {
    int t = c[i]; c[i] = sum; sum += t;
  }
  for (i = 0; i < n; i++)
    tempSA[c[SA[i]+k < n ? RA[SA[i]+k] : 0]++] = SA[i];
  for (i = 0; i < n; i++)
    SA[i] = tempSA[i];
}

void constructSA() {
  int i, k, r;
  for (i = 0; i < n; i++) RA[i] = T[i];
  for (i = 0; i < n; i++) SA[i] = i;
  for (k = 1; k < n; k <<= 1) {
    countingSort(k);
    countingSort(0);
    tempRA[SA[0]] = r = 0;
    for (i = 1; i < n; i++)
      tempRA[SA[i]] =
      (RA[SA[i]] == RA[SA[i-1]] && RA[SA[i]+k] == RA[SA[i-1]+k]) ? r : ++r;
    for (i = 0; i < n; i++)
      RA[i] = tempRA[i];
    if (RA[SA[n-1]] == n-1) break;
} }

// Returns lower_bound and upper_bound-1
// Returns (-1, -1) if not found
ii stringMatching() {
  int lo = 0, hi = n-1, mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res >= 0) hi = mid;
    else          lo = mid + 1;
  }
  if (strncmp(T + SA[lo], P, m) != 0) return ii(-1, -1);
  ii ans; ans.first = lo;
  lo = 0; hi = n - 1; mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res > 0) hi = mid;
    else         lo = mid + 1;
  }
  if (strncmp(T + SA[hi], P, m) != 0) hi--;
  ans.second = hi;
  return ans;
}

struct Q{
	int ind, l , r;
	Q(){}
	Q(int _ind, int _l, int _r) : ind(_ind), l(_l), r(_r) {}
};
vector<int> maping;

void init()
{
	vector<int> pre(n,0);
	int cnt = 1;
	for(int i=0; i<n; i++){
		pre[i] = cnt;
		if(T[i] == '$') cnt++;
	}
	maping.resize(n , 0);
	for(int i=0; i<n; i++){
		int curind = SA[i];
		maping[i] = pre[curind];
		assert(maping[i]);
	}
}
const int maxn = 5e5+10;
vector<Q> vq[maxn];
int ans[maxn];

int fen[maxn];
void update(int pos, int val)
{
	for(int i=pos; i<n+10; i+=i&-i) fen[i] += val;
}

int query(int pos)
{
	int res = 0;
	for(int i=pos; i; i-=i&-i) res += fen[i];
	return res;
}

char s[maxn];
map<int, pair<int,int>> calans;
bool already[maxn];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int cn, q;
	ignore = scanf("%d %d", &cn, &q);
	int cur = 0;
	vector<string> vs;
	for(int i=0; i<cn; i++){
		ignore = scanf("%s", s);
		vs.push_back(s);
		int len = strlen(s);
		for(int j=0; j<len; j++) T[cur++] = s[j];
		T[cur++] = '$';
	}
	n = cur;
	constructSA();
	init();
	for(int i=0; i<q; i++){
		int l, r, k;
		ignore = scanf("%d %d %d", &l, &r, &k);
		k--;
		ii pos;
		if(already[k]) pos = calans[k];
		else{
			int cz = 0;
			for(char ch: vs[k]) P[cz++] = ch;
			m = cz;
			pos = stringMatching();
			calans[k] = pos;
			already[k] = true;
		}
		//cout << vs[k] << " " << pos.first << " " << pos.second << endl;
		vq[pos.second].push_back(Q(i+1, l, r));
		if(pos.first != 0) vq[pos.first-1].push_back(Q(-(i+1), l, r));
	}
	for(int i=0; i<cn; i++) maping[i] = 0;
	//cout << "maping" << endl;
	//for(int i=0; i<n; i++){
		//cout << maping[i] << " ";
	//}
	//cout << endl;
	for(int i=0; i<n; i++){
		int sz = vq[i].size();
		int pos = maping[i];
		if(pos) update(pos, 1);
		for(int j=0; j<sz; j++){
			int l = vq[i][j].l;
			int r = vq[i][j].r;
			int ind = vq[i][j].ind;
			int curr = 0;
			if(ind < 0) curr = -1;
			else curr = 1;
			ind = abs(ind);
			ans[ind] += curr*(query(r) - query(l-1));
		}
	}
	for(int i=1; i<=q; i++) cout << ans[i] << endl;
}


#include<bits/stdc++.h>
using namespace std;

typedef pair<int, int> ii;

#define MAX_N 500010
char T[MAX_N];
int n;
int RA[MAX_N], tempRA[MAX_N];
int SA[MAX_N], tempSA[MAX_N];
int c[MAX_N];

char P[MAX_N];
int m;

void countingSort(int k) {
  int i, sum, maxi = max(300, n);
  memset(c, 0, sizeof c);
  for (i = 0; i < n; i++)
    c[i + k < n ? RA[i + k] : 0]++;
  for (i = sum = 0; i < maxi; i++) {
    int t = c[i]; c[i] = sum; sum += t;
  }
  for (i = 0; i < n; i++)
    tempSA[c[SA[i]+k < n ? RA[SA[i]+k] : 0]++] = SA[i];
  for (i = 0; i < n; i++)
    SA[i] = tempSA[i];
}

void constructSA() {
  int i, k, r;
  for (i = 0; i < n; i++) RA[i] = T[i];
  for (i = 0; i < n; i++) SA[i] = i;
  for (k = 1; k < n; k <<= 1) {
    countingSort(k);
    countingSort(0);
    tempRA[SA[0]] = r = 0;
    for (i = 1; i < n; i++)
      tempRA[SA[i]] =
      (RA[SA[i]] == RA[SA[i-1]] && RA[SA[i]+k] == RA[SA[i-1]+k]) ? r : ++r;
    for (i = 0; i < n; i++)
      RA[i] = tempRA[i];
    if (RA[SA[n-1]] == n-1) break;
} }

// Returns lower_bound and upper_bound-1
// Returns (-1, -1) if not found
ii stringMatching() {
  int lo = 0, hi = n-1, mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res >= 0) hi = mid;
    else          lo = mid + 1;
  }
  if (strncmp(T + SA[lo], P, m) != 0) return ii(-1, -1);
  ii ans; ans.first = lo;
  lo = 0; hi = n - 1; mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res > 0) hi = mid;
    else         lo = mid + 1;
  }
  if (strncmp(T + SA[hi], P, m) != 0) hi--;
  ans.second = hi;
  return ans;
}

struct Q{
	int ind, l , r;
	Q(){}
	Q(int _ind, int _l, int _r) : ind(_ind), l(_l), r(_r) {}
};
vector<int> maping;

void init()
{
	vector<int> pre(n,0);
	int cnt = 1;
	for(int i=0; i<n; i++){
		pre[i] = cnt;
		if(T[i] == '$') cnt++;
	}
	maping.resize(n , 0);
	for(int i=0; i<n; i++){
		int curind = SA[i];
		maping[i] = pre[curind];
		assert(maping[i]);
	}
}
const int maxn = 5e5+10;
vector<Q> vq[maxn];
int ans[maxn];

int fen[maxn];
void update(int pos, int val)
{
	for(int i=pos; i<n+10; i+=i&-i) fen[i] += val;
}

int query(int pos)
{
	int res = 0;
	for(int i=pos; i; i-=i&-i) res += fen[i];
	return res;
}

char s[maxn];
map<int, pair<int,int>> calans;
bool already[maxn];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int cn, q;
	ignore = scanf("%d %d", &cn, &q);
	int cur = 0;
	vector<string> vs;
	for(int i=0; i<cn; i++){
		ignore = scanf("%s", s);
		vs.push_back(s);
		int len = strlen(s);
		for(int j=0; j<len; j++) T[cur++] = s[j];
		T[cur++] = '$';
	}
	n = cur;
	constructSA();
	init();
	for(int i=0; i<q; i++){
		int l, r, k;
		ignore = scanf("%d %d %d", &l, &r, &k);
		k--;
		ii pos;
		if(already[k]) pos = calans[k];
		else{
			int cz = 0;
			for(char ch: vs[k]) P[cz++] = ch;
			m = cz;
			pos = stringMatching();
			calans[k] = pos;
			already[k] = true;
		}
		//cout << vs[k] << " " << pos.first << " " << pos.second << endl;
		vq[pos.second].push_back(Q(i+1, l, r));
		if(pos.first != 0) vq[pos.first-1].push_back(Q(-(i+1), l, r));
	}
	for(int i=0; i<cn; i++) maping[i] = 0;
	//cout << "maping" << endl;
	//for(int i=0; i<n; i++){
		//cout << maping[i] << " ";
	//}
	//cout << endl;
	for(int i=0; i<n; i++){
		int sz = vq[i].size();
		int pos = maping[i];
		if(pos) update(pos, 1);
		for(int j=0; j<sz; j++){
			int l = vq[i][j].l;
			int r = vq[i][j].r;
			int ind = vq[i][j].ind;
			int curr = 0;
			if(ind < 0) curr = -1;
			else curr = 1;
			ind = abs(ind);
			ans[ind] += curr*(query(r) - query(l-1));
		}
	}
	for(int i=1; i<=q; i++) printf("%d\n", ans[i]);
}


#include<bits/stdc++.h>
using namespace std;

typedef pair<int, int> ii;

#define MAX_N 500010
char T[MAX_N];
int n;
int RA[MAX_N], tempRA[MAX_N];
int SA[MAX_N], tempSA[MAX_N];
int c[MAX_N];

char P[MAX_N];
int m;

void countingSort(int k) {
  int i, sum, maxi = max(300, n);
  memset(c, 0, sizeof c);
  for (i = 0; i < n; i++)
    c[i + k < n ? RA[i + k] : 0]++;
  for (i = sum = 0; i < maxi; i++) {
    int t = c[i]; c[i] = sum; sum += t;
  }
  for (i = 0; i < n; i++)
    tempSA[c[SA[i]+k < n ? RA[SA[i]+k] : 0]++] = SA[i];
  for (i = 0; i < n; i++)
    SA[i] = tempSA[i];
}

void constructSA() {
  int i, k, r;
  for (i = 0; i < n; i++) RA[i] = T[i];
  for (i = 0; i < n; i++) SA[i] = i;
  for (k = 1; k < n; k <<= 1) {
    countingSort(k);
    countingSort(0);
    tempRA[SA[0]] = r = 0;
    for (i = 1; i < n; i++)
      tempRA[SA[i]] =
      (RA[SA[i]] == RA[SA[i-1]] && RA[SA[i]+k] == RA[SA[i-1]+k]) ? r : ++r;
    for (i = 0; i < n; i++)
      RA[i] = tempRA[i];
    if (RA[SA[n-1]] == n-1) break;
} }

// Returns lower_bound and upper_bound-1
// Returns (-1, -1) if not found
ii stringMatching() {
  int lo = 0, hi = n-1, mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res >= 0) hi = mid;
    else          lo = mid + 1;
  }
  if (strncmp(T + SA[lo], P, m) != 0) return ii(-1, -1);
  ii ans; ans.first = lo;
  lo = 0; hi = n - 1; mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res > 0) hi = mid;
    else         lo = mid + 1;
  }
  if (strncmp(T + SA[hi], P, m) != 0) hi--;
  ans.second = hi;
  return ans;
}

struct Q{
	int ind, l , r;
	Q(){}
	Q(int _ind, int _l, int _r) : ind(_ind), l(_l), r(_r) {}
};
vector<int> maping;

void init()
{
	vector<int> pre(n,0);
	int cnt = 1;
	for(int i=0; i<n; i++){
		pre[i] = cnt;
		if(T[i] == '$') cnt++;
	}
	maping.resize(n , 0);
	for(int i=0; i<n; i++){
		int curind = SA[i];
		maping[i] = pre[curind];
		assert(maping[i]);
	}
}
const int maxn = 5e5+10;
vector<Q> vq[maxn];
int ans[maxn];

int fen[maxn];
void update(int pos, int val)
{
	for(int i=pos; i<maxn; i+=i&-i) fen[i] += val;
}

int query(int pos)
{
	int res = 0;
	for(int i=pos; i; i-=i&-i) res += fen[i];
	return res;
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int cn;
	cin >> cn;
	int q;
	cin >> q;
	int cur = 0;
	vector<string> vs;
	for(int i=0; i<cn; i++){
		string s;
		cin >> s;
		vs.push_back(s);
		s+='$';
		for(char ch: s) T[cur++] = ch;
	}
	n = cur;
	constructSA();
	init();
	for(int i=0; i<q; i++){
		int l, r, k;
		cin >> l >> r >> k;
		k--;
		int cz = 0;
		for(char ch: vs[k]) P[cz++] = ch;
		m = cz;
		ii pos = stringMatching();
		//cout << vs[k] << " " << pos.first << " " << pos.second << endl;
		vq[pos.second].push_back(Q(i+1, l, r));
		if(pos.first != 0) vq[pos.first-1].push_back(Q(-(i+1), l, r));
	}
	for(int i=0; i<cn; i++) maping[i] = 0;
	//cout << "maping" << endl;
	//for(int i=0; i<n; i++){
		//cout << maping[i] << " ";
	//}
	//cout << endl;
	for(int i=0; i<n; i++){
		int sz = vq[i].size();
		int pos = maping[i];
		if(pos) update(pos, 1);
		for(int j=0; j<sz; j++){
			int l = vq[i][j].l;
			int r = vq[i][j].r;
			int ind = vq[i][j].ind;
			int curr = 0;
			if(ind < 0) curr = -1;
			else curr = 1;
			ind = abs(ind);
			ans[ind] += curr*(query(r) - query(l-1));
		}
	}
	for(int i=1; i<=q; i++) cout << ans[i] << endl;
}


#include<bits/stdc++.h>
using namespace std;

typedef pair<int, int> ii;

#define MAX_N 500010
char T[MAX_N];
int n;
int RA[MAX_N], tempRA[MAX_N];
int SA[MAX_N], tempSA[MAX_N];
int c[MAX_N];

char P[MAX_N];
int m;

void countingSort(int k) {
  int i, sum, maxi = max(300, n);
  memset(c, 0, sizeof c);
  for (i = 0; i < n; i++)
    c[i + k < n ? RA[i + k] : 0]++;
  for (i = sum = 0; i < maxi; i++) {
    int t = c[i]; c[i] = sum; sum += t;
  }
  for (i = 0; i < n; i++)
    tempSA[c[SA[i]+k < n ? RA[SA[i]+k] : 0]++] = SA[i];
  for (i = 0; i < n; i++)
    SA[i] = tempSA[i];
}

void constructSA() {
  int i, k, r;
  for (i = 0; i < n; i++) RA[i] = T[i];
  for (i = 0; i < n; i++) SA[i] = i;
  for (k = 1; k < n; k <<= 1) {
    countingSort(k);
    countingSort(0);
    tempRA[SA[0]] = r = 0;
    for (i = 1; i < n; i++)
      tempRA[SA[i]] =
      (RA[SA[i]] == RA[SA[i-1]] && RA[SA[i]+k] == RA[SA[i-1]+k]) ? r : ++r;
    for (i = 0; i < n; i++)
      RA[i] = tempRA[i];
    if (RA[SA[n-1]] == n-1) break;
} }

// Returns lower_bound and upper_bound-1
// Returns (-1, -1) if not found
ii stringMatching() {
  int lo = 0, hi = n-1, mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res >= 0) hi = mid;
    else          lo = mid + 1;
  }
  if (strncmp(T + SA[lo], P, m) != 0) return ii(-1, -1);
  ii ans; ans.first = lo;
  lo = 0; hi = n - 1; mid = lo;
  while (lo < hi) {
    mid = (lo + hi) / 2;
    int res = strncmp(T + SA[mid], P, m);
    if (res > 0) hi = mid;
    else         lo = mid + 1;
  }
  if (strncmp(T + SA[hi], P, m) != 0) hi--;
  ans.second = hi;
  return ans;
}

struct Q{
	int ind, l , r;
	Q(){}
	Q(int _ind, int _l, int _r) : ind(_ind), l(_l), r(_r) {}
};
vector<int> maping;

void init()
{
	vector<int> pre(n,0);
	int cnt = 1;
	for(int i=0; i<n; i++){
		pre[i] = cnt;
		if(T[i] == '$') cnt++;
	}
	maping.resize(n , 0);
	for(int i=0; i<n; i++){
		int curind = SA[i];
		maping[i] = pre[curind];
		assert(maping[i]);
	}
}
const int maxn = 5e5+10;
vector<Q> vq[maxn];
int ans[maxn];

int fen[maxn];
void update(int pos, int val)
{
	for(int i=pos; i<maxn; i+=i&-i) fen[i] += val;
}

int query(int pos)
{
	int res = 0;
	for(int i=pos; i; i-=i&-i) res += fen[i];
	return res;
}

char s[maxn];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int cn, q;
	ignore = scanf("%d %d", &cn, &q);
	int cur = 0;
	vector<string> vs;
	for(int i=0; i<cn; i++){
		ignore = scanf("%s", s);
		vs.push_back(s);
		int len = strlen(s);
		for(int j=0; j<len; j++) T[cur++] = s[j];
		T[cur++] = '$';
	}
	n = cur;
	constructSA();
	init();
	for(int i=0; i<q; i++){
		int l, r, k;
		ignore = scanf("%d %d %d", &l, &r, &k);
		k--;
		int cz = 0;
		for(char ch: vs[k]) P[cz++] = ch;
		m = cz;
		ii pos = stringMatching();
		//cout << vs[k] << " " << pos.first << " " << pos.second << endl;
		vq[pos.second].push_back(Q(i+1, l, r));
		if(pos.first != 0) vq[pos.first-1].push_back(Q(-(i+1), l, r));
	}
	for(int i=0; i<cn; i++) maping[i] = 0;
	//cout << "maping" << endl;
	//for(int i=0; i<n; i++){
		//cout << maping[i] << " ";
	//}
	//cout << endl;
	for(int i=0; i<n; i++){
		int sz = vq[i].size();
		int pos = maping[i];
		if(pos) update(pos, 1);
		for(int j=0; j<sz; j++){
			int l = vq[i][j].l;
			int r = vq[i][j].r;
			int ind = vq[i][j].ind;
			int curr = 0;
			if(ind < 0) curr = -1;
			else curr = 1;
			ind = abs(ind);
			ans[ind] += curr*(query(r) - query(l-1));
		}
	}
	for(int i=1; i<=q; i++) cout << ans[i] << endl;
}


#include <bits/stdc++.h>
#define FOR(i, n) for(int i = 0; i < (n); ++i)
#define REP(i, a, b) for(int i = (a); i < (b); ++i)
#define TRAV(i, a) for(auto & i : (a))
#define SZ(x) ((int)(x).size())
#define PR std::pair
#define MP std::make_pair
#define X first
#define Y second
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::vector<int> VI;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
template<typename T>
using ordered_set = tree<
T,
null_type,
std::less<T>,
rb_tree_tag,
tree_order_statistics_node_update>;

//X.find_by_order(k); - zwraca iterator na k-ty element (numeracja od zerowego)
//X.order_of_key(k); - zwraca liczbę elementów ostro mniejszych niż k

// https://doi.org/10.1145/1217856.1217858
inline bool leq(int a1, int a2, int b1, int b2){
	return a1 < b1 || (a1 == b1 && a2 <= b2);
}
inline bool leq(int a1, int a2, int a3, int b1, int b2, int b3){
	return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));
}
void radix(int *a, int *b, int *r, int n, int K){
	int *c = new int[K+1];
	for(int i = 0; i <= K; ++i) c[i] = 0;
	for(int i = 0; i < n; ++i) c[r[a[i]]]++;
	for(int i = 0, sum = 0, t = 0; i <= K; ++i) t = c[i], c[i] = sum, sum += t;
	for(int i = 0; i < n; ++i) b[c[r[a[i]]]++] = a[i];
	delete [] c;
}
// A = {1..K}
// T[0..n-1] - word
// T[n] = T[n+1] = T[n+2] = 0
// SA[0..n-1] - output
void suffix_array(int *T, int *SA, int n, int K){
	if(n == 1){
		SA[0] = 0;
		return;
	}
	int n0 = (n+2)/3, n1 = (n+1)/3, n2 = n/3, n02=n0+n2;
	int *R = new int[n02+3];
	int *SA12 = new int[n02+3];
	int *R0 = new int[n02];
	int *SA0 = new int[n02];
	for(int i = 0; i < 3; ++i) R[n02+i] = SA12[n02+i] = 0;
	for(int i = 0, j = 0; i < n+n0-n1; ++i) if(i%3) R[j++] = i;
	radix(R, SA12, T+2, n02, K);
	radix(SA12, R, T+1, n02, K);
	radix(R, SA12, T, n02, K);
	int name = 0, c0 = -1, c1 = -1, c2 = -1;
	for(int i = 0; i < n02; ++i){
		if(T[SA12[i]] != c0 || T[SA12[i]+1] != c1 || T[SA12[i]+2] != c2)
			name++, c0 = T[SA12[i]], c1 = T[SA12[i]+1], c2 = T[SA12[i]+2];
		if(SA12[i]%3 == 1) R[SA12[i]/3] = name;
		else R[SA12[i]/3+n0] = name;
	}
	if(name < n02){
		suffix_array(R, SA12, n02, name);
		for(int i = 0; i < n02; ++i) R[SA12[i]] = i+1;
	}else for(int i = 0; i < n02; ++i) SA12[R[i]-1] = i;
	for(int i = 0, j = 0; i < n02; ++i) if(SA12[i] < n0) R0[j++] = 3*SA12[i];
	radix(R0, SA0, T, n0, K);
	for(int p = 0, t = n0-n1, k = 0; k < n; ++k){
		#define GetI() (SA12[t] < n0 ? SA12[t] * 3 + 1 : (SA12[t] - n0) * 3 + 2)
		int i = GetI();
		int j = SA0[p];
		if(SA12[t] < n0 ?
			leq(T[i], R[SA12[t]+n0], T[j], R[j/3]) :
			leq(T[i], T[i+1], R[SA12[t]-n0+1], T[j], T[j+1], R[j/3+n0])){
			SA[k] = i, t++;
			if(t == n02) for(k++; p < n0; p++, k++) SA[k] = SA0[p];
		}else{
			SA[k] = j; p++;
			if(p == n0) for(k++; t < n02; t++, k++) SA[k] = GetI();
		}
	}
	delete[] R; delete[] SA12; delete[] SA0; delete[] R0;
}

// lcp[0..n-2] - output
void kasai(int *T, int *sa, int *lcp, int n){
	int k = 0;
	int *rank = new int[n];
	for(int i = 0; i < n; ++i) rank[sa[i]] = i;
	for(int i = 0; i < n; ++i, k?k--:0){
		if(rank[i] == n-1){
			k = 0; continue;
		}
		int j = sa[rank[i]+1];
		while(i+k<n && j+k<n && T[i+k] == T[j+k]) k++;
		lcp[rank[i]] = k;
	}
	delete[] rank;
}

// A = {1..K}
// T[0..n-1] - word
// T[n] = T[n+1] = T[n+2] = 0
// SA[0..n-1] - output
// void suffix_array(int *T, int *SA, int n, int K)

const int M = 400010;
int T[M], sa[M], lcp[M], rank[M];
/*
const int MOD = 1000000007;
const int P = 5315;
int hash[M], pw[M];

bool same(int a, int b, int l){
	a++;b++;
	int h1 = (hash[a+l-1]-hash[a]*pw[l])%MOD;
	if(h1 < 0) h1 += MOD;
	int h2 = (hash[b+l-1]-hash[b]*pw[l])%MOD;
	if(h2 < 0) h2 += MOD;
	return h1 == h2;
}*/

const int INF = 1000000666;
struct Tree{
	int BOTTOM;
	VI t;
	int szzz;
	void build(VI A){
		szzz = SZ(A);
		BOTTOM = 1;
		while(BOTTOM < SZ(A)) BOTTOM *= 2;
		t.resize(BOTTOM*2, INF);
		FOR(i, SZ(A)) t[i+BOTTOM] = A[i];
		for(int i = BOTTOM-1; i >= 1; --i) t[i] = std::min(t[i<<1], t[(i<<1)+1]);
	}

	int query(int a, int b){
		a += BOTTOM;
		b += BOTTOM+1;
		int ret = INF;
		while(a < b){
			if(a&1) ret = std::min(ret, t[a++]);
			if(b&1) ret = std::min(ret, t[--b]);
			a>>=1;b>>=1;
		}
		return ret;
	}
};

int main(){
	std::ios_base::sync_with_stdio(false);
	std::cin.tie(0);
	int n, q;
	std::cin >> n >> q;
	std::vector<VI> A(n);

	VI slo;
	VI pss(n);
	FOR(i, n){
		std::string s;
		std::cin >> s;
		pss[i] = SZ(slo);
		TRAV(c, s) A[i].push_back(SZ(slo)), slo.push_back(c-'a'+2);
		slo.push_back(1);
	}
/*
	hash[0] = 0;
	pw[0] = 1;
	REP(i, 1, M) pw[i] = (1ll*pw[i-1]*P)%MOD;
	FOR(i, SZ(slo)){
		hash[i+1] = (1ll*hash[i]*P + slo[i])%MOD;
	}
*/
	FOR(i, SZ(slo)) T[i] = slo[i];
	REP(i, SZ(slo), SZ(slo)+3) T[i] = 0;

	suffix_array(T, sa, SZ(slo), 50);
	kasai(T, sa, lcp, SZ(slo));

	FOR(i, SZ(slo)) rank[sa[i]] = i;
	/*FOR(i, SZ(slo)) std::cout << slo[i] << " ";
	std::cout << "\n";
	FOR(i, SZ(slo)) std::cout << rank[i] << " ";
	std::cout << "\n";*/

	Tree tree;
	VI hehe(SZ(slo)-1);
	FOR(i, SZ(slo)-1) hehe[i] = lcp[i];
	tree.build(hehe);

	VI ans(q);
	std::vector<std::vector<PII> > events(n);
	std::vector<PII> prze(q);

/*
	FOR(i, SZ(slo)) std::cerr << (slo[i] == 1 ? "|" : std::to_string(slo[i])) << " ";
	std::cerr << std::endl;
*/
	FOR(i, q){
		int l, r, k;
		std::cin >> l >> r >> k;
		l--;r--;k--;
		int p = rank[pss[k]];

		int left = 0;
		int right = p;
		while(left < right){
			int mid = (left+right)/2;
			if(tree.query(mid, p-1) < SZ(A[k])) left = mid+1;
			else right = mid;
		}

		int L = left;

		left = p;
		right = SZ(slo);
		while(left < right){
			int mid = (left+right)/2;
			if(tree.query(p, mid-1) >= SZ(A[k])) left = mid+1;
			else right = mid;
		}
		left--;

		int R = left;

		prze[i] = MP(L, R);

		if(l > 0) events[l-1].push_back(MP(i, -1));
		events[r].push_back(MP(i, 1));
	}

	ordered_set<int> set;

	auto ask = [&](int a, int b){
		return set.order_of_key(b+1) - set.order_of_key(a);
	};

	FOR(i, n){
		TRAV(j, A[i]) set.insert(rank[j]);
		TRAV(ev, events[i]){
			int a = prze[ev.X].X;
			int b = prze[ev.X].Y;
			ans[ev.X] += ev.Y*ask(a, b);
		}
	}

	FOR(i, q) std::cout << ans[i] << "\n";

	return 0;
}