#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
const int INF = 1000000001;
const long long int LLINF = 1000000000000000001LL;
const int ALPH = ('z' - 'a' + 1);
struct node {
  int s[ALPH];
  int flink = -1, dictlink = -1, par;
  char parChar;
  int word = -1;
  node(int par = -1, char parChar = '$') : par(par), parChar(parChar) {
    for (int i = (0); i <= (ALPH - 1); ++i) s[i] = -1;
  }
  int getSon(char c) { return s[c - 'a']; }
  void setSon(char c, int k) { s[c - 'a'] = k; }
};
struct Aho {
  vector<node> T;
  Aho(const vector<string> &V) : T(1) {
    for (int i = (0); i <= ((int)(V).size() - 1); ++i) add(V[i], i);
    queue<int> Q;
    Q.push(0);
    while (!Q.empty()) {
      int q = Q.front();
      Q.pop();
      for (int i = (0); i <= (ALPH - 1); ++i)
        if (T[q].s[i] != -1) {
          Q.push(T[q].s[i]);
        }
      if (!q) continue;
      int v = T[T[q].par].flink;
      char pChar = T[q].parChar;
      while (v > -1 && T[v].getSon(pChar) == -1) {
        v = T[v].flink;
      }
      T[q].flink = (v > -1 ? T[v].getSon(pChar) : 0);
      v = T[q].flink;
      if (T[v].word == -1) {
        v = T[v].dictlink;
      }
      T[q].dictlink = v;
    }
  }
  void add(const string &s, int ind) {
    int it = 0, v = 0, len = (int)(s).size();
    while (it < len && T[v].getSon(s[it]) != -1) {
      v = T[v].getSon(s[it++]);
    }
    while (it < len) {
      T.push_back(node(v, s[it]));
      T[v].setSon(s[it++], (int)(T).size() - 1);
      v = (int)(T).size() - 1;
    }
    T[v].word = ind;
  }
  vector<int> longestOccurences(string &s) {
    int v = 0;
    vector<int> occ;
    for (char c : s) {
      while (v && T[v].getSon(c) == -1) {
        v = T[v].flink;
      }
      v = max(T[v].getSon(c), 0);
      if (T[v].word != -1) {
        occ.push_back(T[v].word);
      } else if (T[v].dictlink != -1) {
        occ.push_back(T[T[v].dictlink].word);
      }
    }
    return occ;
  }
};
struct ITree {
  vector<int> T;
  int p = 1;
  ITree(int n) {
    while (p < n) p *= 2;
    T.resize(2 * p - 1, 0);
  }
  int par(int x) { return (x - 1) / 2; }
  void Insert(int l, int r, int v) {
    l += p - 1;
    r += p - 1;
    T[l] += v;
    if (l < r) T[r] += v;
    while (par(l) != par(r)) {
      if (l % 2 == 1) T[l + 1] += v;
      if (r % 2 == 0) T[r - 1] += v;
      l = par(l);
      r = par(r);
    }
  }
  int Query(int l) {
    l += p - 1;
    int ans = T[l];
    while (l) {
      l = par(l);
      ans += T[l];
    }
    return ans;
  }
};
vector<string> reverseAll(vector<string> &T) {
  vector<string> res;
  for (string s : T) {
    reverse(s.begin(), s.end());
    res.push_back(s);
  }
  return res;
}
void dfs(int v, int lowest, vector<node> &T, vector<int> &res) {
  int w = T[v].word;
  if (w != -1) {
    res[w] = lowest;
    lowest = w;
  }
  for (int i = (0); i <= (ALPH - 1); ++i)
    if (T[v].s[i] != -1) {
      dfs(T[v].s[i], lowest, T, res);
    }
}
vector<vector<int> > makeGraph(int n, vector<int> &par) {
  vector<vector<int> > G(n);
  for (int i = (0); i <= (n - 1); ++i)
    if (par[i] != -1) {
      G[par[i]].push_back(i);
    }
  return G;
}
void dfsInOut(int v, vector<vector<int> > &G, vector<int> &in, vector<int> &out,
              int &t) {
  in[v] = t++;
  for (int u : G[v]) {
    dfsInOut(u, G, in, out, t);
  }
  out[v] = t++;
}
int main() {
  ios_base::sync_with_stdio(0);
  int n, k;
  cin >> n >> k;
  vector<string> name(k);
  for (int i = (0); i <= (k - 1); ++i) cin >> name[i];
  vector<int> suff(k);
  {
    Aho revAho(reverseAll(name));
    dfs(0, -1, revAho.T, suff);
  }
  int t = 0;
  vector<int> in(k), out(k);
  {
    vector<vector<int> > G = makeGraph(k, suff);
    for (int i = (0); i <= (k - 1); ++i)
      if (suff[i] == -1) {
        dfsInOut(i, G, in, out, t);
      }
  }
  ITree tree(t);
  for (int i = (0); i <= (k - 1); ++i) {
    tree.Insert(in[i], out[i], 1);
  }
  vector<bool> is(k, 1);
  Aho aho(name);
  while (n--) {
    char type;
    cin >> type;
    int id;
    string text;
    switch (type) {
      case '+':
        cin >> id;
        id--;
        if (!is[id]) {
          tree.Insert(in[id], out[id], 1);
          is[id] = 1;
        }
        break;
      case '-':
        cin >> id;
        id--;
        if (is[id]) {
          tree.Insert(in[id], out[id], -1);
          is[id] = 0;
        }
        break;
      case '?':
        cin >> text;
        auto occ = aho.longestOccurences(text);
        long long int ans = 0;
        for (int id : occ) {
          ans += tree.Query(in[id]);
        }
        cout << ans << '\n';
        break;
    }
  }
  return 0;
}
