#include <bits/stdc++.h>
using namespace std;
template <class T>
inline void amin(T &x, const T &y) {
  if (y < x) x = y;
}
template <class T>
inline void amax(T &x, const T &y) {
  if (x < y) x = y;
}
struct PMA {
  struct Node {
    Node *ch, *sib, *fail;
    char c;
    long long cnt;
    int aux;
    Node() : ch(0), sib(0), fail(0), c(0), cnt(0) {}
  };
  Node *root, *nodes;
  int nodes_i;
  PMA() {}
  PMA(const vector<string> &d, const vector<int> &g) {
    int len = 0;
    for (int i = 0, i_len = (d.size()); i < i_len; ++i) len += d[i].size();
    len++;
    nodes_i = 0;
    nodes = new Node[len];
    root = new_node();
    for (int i = 0, i_len = (d.size()); i < i_len; ++i) {
      Node *x = root;
      const string &s = d[i];
      for (int j = 0, j_len = (s.size()); j < j_len; ++j)
        x = get_child(x, s[j], true);
      x->cnt += g[i];
    }
    vector<Node *> ord = bfs();
    root->fail = root;
    {
      Node *x = root->ch;
      while (x) {
        x->fail = root;
        ord.push_back(x);
        x = x->sib;
      }
    }
    for (int i = 1; i < nodes_i; i++) {
      Node *x = ord[i];
      Node *ch = x->ch;
      while (ch) {
        Node *f = x->fail, *y;
        while (!(y = get_child(f, ch->c)) && f != root) f = f->fail;
        ch->fail = y ?: root;
        ch = ch->sib;
      }
    }
    for (int i = 0, i_len = (nodes_i); i < i_len; ++i)
      ord[i]->cnt += ord[i]->fail->cnt;
  }
  Node *new_node() { return &nodes[nodes_i++]; }
  Node *get_child(Node *x, char c, bool create = false) {
    if (!x->ch) {
      if (create) {
        x->ch = new_node();
        x->ch->c = c;
      }
      return x->ch;
    } else {
      x = x->ch;
      while (true) {
        if (x->c == c) {
          return x;
        } else if (x->sib) {
          x = x->sib;
        } else {
          if (create) {
            x->sib = new_node();
            x->sib->c = c;
          }
          return x->sib;
        }
      }
    }
  }
  vector<Node *> bfs() {
    vector<Node *> ord;
    ord.reserve(nodes_i);
    ord.push_back(root);
    for (int i = 0; i < nodes_i; i++) {
      Node *x = ord[i];
      Node *ch = x->ch;
      while (ch) {
        ord.push_back(ch);
        ch = ch->sib;
      }
    }
    return ord;
  }
  long long match(const string &s) {
    Node *x = root;
    long long ret = 0;
    for (int i = 0, i_len = (s.size()); i < i_len; ++i) {
      Node *y = NULL;
      while (!(y = get_child(x, s[i])) && x != root) x = x->fail;
      x = y ?: root;
      ret += x->cnt;
    }
    return ret;
  }
  void clear() {
    delete[] nodes;
    root = nodes = NULL;
    nodes_i = 0;
  }
};
int N;
PMA Z[30];
char op[99], buf[300111];
vector<string> D[30];
vector<int> G[30];
void add(const string &s, int x) {
  vector<string> cur_d(1, s);
  vector<int> cur_g(1, x);
  for (int i = 0, i_len = (20); i < i_len; ++i) {
    if (D[i].empty()) {
      D[i] = cur_d;
      G[i] = cur_g;
      Z[i] = PMA(D[i], G[i]);
      break;
    } else {
      cur_d.insert(cur_d.end(), D[i].begin(), D[i].end());
      cur_g.insert(cur_g.end(), G[i].begin(), G[i].end());
      D[i].clear();
      G[i].clear();
      Z[i].clear();
    }
  }
}
long long query(const string &s) {
  long long ret = 0;
  for (int i = 0, i_len = (20); i < i_len; ++i) {
    if (!D[i].empty()) {
      ret += Z[i].match(s);
    }
  }
  return ret;
}
int main() {
  scanf("%d", &N);
  for (int $ = 0, $_len = (N); $ < $_len; ++$) {
    scanf("%s%s", op, buf);
    if (op[0] == '1') {
      add(buf, 1);
    } else if (op[0] == '2') {
      add(buf, -1);
    } else {
      printf("%lld\n", query(buf));
      fflush(stdout);
    }
  }
  return 0;
}
