#include <bits/stdc++.h>
using std::cerr;
using std::deque;
using std::endl;
using std::fill;
using std::ios_base;
using std::istream;
using std::iterator;
using std::make_pair;
using std::map;
using std::max;
using std::min;
using std::numeric_limits;
using std::ostream;
using std::pair;
using std::priority_queue;
using std::set;
using std::sort;
using std::string;
using std::swap;
using std::vector;
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
namespace dalt {};
namespace other {};
namespace dalt {
template <class E, class M>
class BitDecomposeFramework {
 private:
  vector<E *> _data;
  M _merger;
  void add(E *e, int index) {
    if (_data.size() == index) {
      _data.push_back(e);
      return;
    }
    if (_data[index] == NULL) {
      _data[index] = e;
      return;
    }
    add(_merger(_data[index], e), index + 1);
    _data[index] = NULL;
  }

 public:
  void add(E *e) { add(e, 0); }
  template <class C>
  void consume(C &c) {
    for (E *e : _data) {
      if (e) {
        c(e);
      }
    }
  }
};
}  // namespace dalt
namespace dalt {
template <int L, int R>
class ACNode {
 public:
  ACNode *next[R - L + 1];
  ACNode *fail;
  ACNode *father;
  int index;
  int id;
  int cnt;
  int preSum;
  ACNode() {
    memset(next, 0, sizeof(next));
    fail = father = 0;
    index = id = cnt = preSum = 0;
  }
  int getId() { return id; }
  int getCnt() { return cnt; }
  void decreaseCnt() { cnt--; }
  void increaseCnt() { cnt++; }
  int getPreSum() { return preSum; }
};
template <int L, int R>
class ACAutomaton {
 private:
  ACNode<L, R> *root;
  ACNode<L, R> *buildLast;
  ACNode<L, R> *matchLast;
  vector<ACNode<L, R> *> allNodes;
  ACNode<L, R> *addNode() {
    ACNode<L, R> *node = new ACNode<L, R>();
    node->id = allNodes.size();
    allNodes.push_back(node);
    return node;
  }

 public:
  ACNode<L, R> *getBuildLast() { return buildLast; }
  ACNode<L, R> *getMatchLast() { return matchLast; }
  vector<ACNode<L, R> *> getAllNodes() { return allNodes; }
  ACAutomaton() {
    root = buildLast = matchLast = 0;
    root = addNode();
  }
  void beginBuilding() { buildLast = root; }
  void endBuilding() {
    deque<ACNode<L, R> *> que;
    for (int i = 0; i < (R - L + 1); i++) {
      if (root->next[i] != NULL) {
        que.push_back(root->next[i]);
      }
    }
    while (!que.empty()) {
      ACNode<L, R> *head = que.front();
      que.pop_front();
      ACNode<L, R> *fail = visit(head->father->fail, head->index);
      if (fail == NULL) {
        head->fail = root;
      } else {
        head->fail = fail->next[head->index];
      }
      head->preSum = head->cnt + head->fail->preSum;
      for (int i = 0; i < (R - L + 1); i++) {
        if (head->next[i] != NULL) {
          que.push_back(head->next[i]);
        }
      }
    }
    for (int i = 0; i < (R - L + 1); i++) {
      if (root->next[i] != NULL) {
        que.push_back(root->next[i]);
      } else {
        root->next[i] = root;
      }
    }
    while (!que.empty()) {
      ACNode<L, R> *head = que.front();
      que.pop_front();
      for (int i = 0; i < (R - L + 1); i++) {
        if (head->next[i] != NULL) {
          que.push_back(head->next[i]);
        } else {
          head->next[i] = head->fail->next[i];
        }
      }
    }
  }
  ACNode<L, R> *visit(ACNode<L, R> *trace, int index) {
    while (trace != NULL && trace->next[index] == NULL) {
      trace = trace->fail;
    }
    return trace;
  }
  void build(char c) {
    int index = c - L;
    if (buildLast->next[index] == NULL) {
      ACNode<L, R> *node = addNode();
      node->father = buildLast;
      node->index = index;
      buildLast->next[index] = node;
    }
    buildLast = buildLast->next[index];
  }
  void beginMatching() { matchLast = root; }
  void match(char c) {
    int index = c - L;
    matchLast = matchLast->next[index];
  }
};
}  // namespace dalt
using namespace dalt;
typedef pair<vector<string *>, ACAutomaton<'a', 'z'>> pva;
void prepare(pva *ans) {
  for (string *s : ans->first) {
    ans->second.beginBuilding();
    for (char c : (*s)) {
      ans->second.build(c);
    }
    ans->second.getBuildLast()->increaseCnt();
  }
  ans->second.endBuilding();
}
struct Merger {
  pva *operator()(pva *a, pva *b) {
    pva *ans = new pva();
    ans->first.insert(ans->first.end(), a->first.begin(), a->first.end());
    ans->first.insert(ans->first.end(), b->first.begin(), b->first.end());
    prepare(ans);
    return ans;
  }
};
struct Consumer {
  long long ans;
  string *s;
  Consumer(string *str) : ans(0), s(str) {}
  void operator()(pva *a) {
    a->second.beginMatching();
    for (char c : (*s)) {
      a->second.match(c);
      ans += a->second.getMatchLast()->getPreSum();
    }
  }
};
class FStringSetQueries {
 public:
  void solve(std::istream &in, std::ostream &out) {
    int m;
    in >> m;
    BitDecomposeFramework<pva, Merger> addF;
    BitDecomposeFramework<pva, Merger> subF;
    for (int i = 0; i < m; i++) {
      int t;
      string *s = new string();
      in >> t >> (*s);
      if (t == 1) {
        pva *p = new pva();
        p->first.push_back(s);
        prepare(p);
        addF.add(p);
      } else if (t == 2) {
        pva *p = new pva();
        p->first.push_back(s);
        prepare(p);
        subF.add(p);
      } else {
        Consumer pos(s), neg(s);
        addF.consume(pos);
        subF.consume(neg);
        out << pos.ans - neg.ans << endl;
        out.flush();
      }
    }
  }

 private:
};
int main() {
  std::ios_base::sync_with_stdio(false);
  std::cin.tie(NULL);
  std::cout << std::setiosflags(std::ios::fixed);
  std::cout << std::setprecision(15);
  FStringSetQueries solver;
  std::istream &in(std::cin);
  std::ostream &out(std::cout);
  solver.solve(in, out);
  return 0;
}
