#include <bits/stdc++.h>
using namespace std;
const int Trie_MAX = 26;
const int Trie_Zero = 'a';
struct Trie {
  struct Node {
    int n;
    int suf;
    int b;
    int next[Trie_MAX];
    int back;
    Node() {
      n = 0;
      suf = 0;
      b = 0;
      fill(next, next + Trie_MAX, -1);
      back = 0;
    }
  };
  vector<Node> tree;
  Trie() { tree.push_back(Node()); }
  void insert(char *s) {
    int x = 0;
    for (int i = 0; s[i] != 0; ++i) {
      s[i] -= Trie_Zero;
      ++tree[x].n;
      if (tree[x].next[s[i]] > 0) {
        x = tree[x].next[s[i]];
      } else {
        int y = (int)tree.size();
        tree.push_back(Node());
        tree[x].next[s[i]] = y;
        x = y;
      }
      s[i] += Trie_Zero;
    }
    tree[x].b = 1;
  }
};
struct AC {
  Trie root;
  void insert(char *s) { root.insert(s); }
  void build() {
    queue<int> Q;
    for (int i = 0; i < Trie_MAX; ++i) {
      if (root.tree[0].next[i] > 0) {
        Q.push(root.tree[0].next[i]);
        root.tree[root.tree[0].next[i]].suf =
            root.tree[root.tree[0].next[i]].b ? 1 : 0;
        root.tree[root.tree[0].next[i]].back = 0;
      } else {
        root.tree[0].next[i] = 0;
      }
    }
    while (!Q.empty()) {
      int x = Q.front();
      Q.pop();
      for (int i = 0; i < Trie_MAX; ++i) {
        if (root.tree[x].next[i] > 0) {
          Q.push(root.tree[x].next[i]);
          int y = root.tree[x].back;
          while (root.tree[y].next[i] < 0) y = root.tree[y].back;
          root.tree[root.tree[x].next[i]].suf =
              (root.tree[root.tree[x].next[i]].b ? 1 : 0) +
              root.tree[root.tree[y].next[i]].suf;
          root.tree[root.tree[x].next[i]].back = root.tree[y].next[i];
        }
      }
    }
  }
  int find(char *s) {
    int x = 0;
    int y = 0;
    for (int i = 0; s[i] != 0; ++i) {
      s[i] -= Trie_Zero;
      while (root.tree[x].next[s[i]] < 0) {
        x = root.tree[x].back;
      }
      x = root.tree[x].next[s[i]];
      y += root.tree[x].suf;
      s[i] += Trie_Zero;
    }
    return y;
  }
};
template <class Data, class T>
class DDS {
  struct Item {
    vector<T> Item_V;
    Data D;
    bool flag;
    Item() { flag = 0; }
    void put(T a) {
      Item_V.push_back(a);
      D.insert((char *)a.c_str());
      flag = 1;
    }
  };
  vector<Item> DDS_V;

 public:
  void put(T a) {
    int s = DDS_V.size();
    int k = 0;
    while (k < s && (int)DDS_V[s - k - 1].Item_V.size() == (1 << k)) ++k;
    if (k == 0) {
      Item b;
      b.put(a);
      DDS_V.push_back(b);
      return;
    }
    for (int i = 1; i < k; ++i) {
      for (typename vector<T>::iterator it = DDS_V[s - i].Item_V.begin();
           it != DDS_V[s - i].Item_V.end(); ++it) {
        DDS_V[s - k].put(*it);
      }
      DDS_V.pop_back();
    }
    DDS_V[s - k].put(a);
  }
  long long get(char *s) {
    long long x = 0;
    for (typename vector<Item>::iterator it = DDS_V.begin(); it != DDS_V.end();
         ++it) {
      if ((*it).flag) {
        (*it).D.build();
        (*it).flag = 0;
      }
      x += (*it).D.find(s);
    }
    return x;
  }
};
int m, t;
char s[300010];
DDS<AC, string> in, out;
int main() {
  scanf("%d", &m);
  for (int i = (int)(0); i < (int)(m); ++i) {
    scanf("%d %s", &t, s);
    if (t == 1) {
      in.put(string(s));
    } else if (t == 2) {
      out.put(string(s));
    } else {
      printf("%lld\n", in.get(s) - out.get(s));
      fflush(stdout);
    }
  }
  return 0;
}
