#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const long double pi = acos(-1.0);
const string debug_line = "yolo";
const long double PI = acos((long double)-1.0);
const int statesLimit = 50010;
const int characterLimit = 26;
int f[statesLimit];
int g[statesLimit][26];
vector<int> output[statesLimit];
int numStates;
struct ahoCorasick {
  vector<int> f;
  vector<vector<int> > g;
  vector<int> output;
  int wordCount;

 public:
  ahoCorasick() { wordCount = -1; }
  void clearData() {
    f.clear();
    g.clear();
    output.clear();
    wordCount = -1;
  }
  void mergeOutput(int state1, int state2) { output[state1] += output[state2]; }
  void buildMatchingMachine(const vector<string> &words) {
    int states = 1;
    int totalSize = 0;
    for (int(i) = (0); (i) < (words.size()); ++(i)) {
      totalSize += words[i].size();
    }
    wordCount = 0;
    f.resize(totalSize + 10, -1);
    g.resize(totalSize + 10, vector<int>(26, -1));
    output.resize(totalSize + 1);
    for (int(i) = (0); (i) < (words.size()); ++(i)) {
      const string &word = words[i];
      int currentState = 0;
      for (int(index) = (0); (index) < (word.size()); ++(index)) {
        int c = word[index] - 'a';
        if (g[currentState][c] == -1) {
          g[currentState][c] = states++;
        }
        currentState = g[currentState][c];
      }
      output[currentState]++;
    }
    for (int(index) = (0); (index) < (characterLimit); ++(index)) {
      if (g[0][index] == -1) {
        g[0][index] = 0;
      }
    }
    queue<int> Q1;
    for (int(index) = (0); (index) < (characterLimit); ++(index)) {
      if (g[0][index] != -1 && g[0][index] != 0) {
        f[g[0][index]] = 0;
        Q1.push(g[0][index]);
      }
    }
    while (!Q1.empty()) {
      int state = Q1.front();
      Q1.pop();
      for (int(index) = (0); (index) < (characterLimit); ++(index)) {
        if (g[state][index] != -1) {
          int failure = f[state];
          while (g[failure][index] == -1) {
            failure = f[failure];
          }
          failure = g[failure][index];
          f[g[state][index]] = failure;
          mergeOutput(g[state][index], failure);
          Q1.push(g[state][index]);
        }
      }
    }
    numStates = states;
  };
  int findNextState(int currentState, char nextCharacter) {
    int answer = currentState;
    int nextChar = nextCharacter - 'a';
    while (g[answer][nextChar] == -1) {
      answer = f[answer];
    }
    return g[answer][nextChar];
  };
  long long findOccurance(string sample) {
    if (wordCount == -1) {
      return 0;
    }
    long long answer1 = 0;
    int currState = 0;
    for (int(i) = (0); (i) < (sample.size()); ++(i)) {
      currState = findNextState(currState, sample[i]);
      answer1 = answer1 + (long long)output[currState];
    }
    return answer1;
  }
};
struct blocks {
  vector<string> block[30];
  vector<ahoCorasick> root;
  blocks() {
    for (int(i) = (0); (i) < (30); ++(i)) {
      block[i].clear();
      ahoCorasick ac;
      root.push_back(ac);
    }
  }
  void insert(string s) {
    vector<string> curr(1, s);
    for (int(i) = (0); (i) < (30); ++(i)) {
      if (block[i].size() == curr.size()) {
        curr.insert(curr.end(), block[i].begin(), block[i].end());
        block[i].clear();
        root[i].clearData();
      } else {
        block[i] = curr;
        root[i].clearData();
        root[i].buildMatchingMachine(block[i]);
        break;
      }
    }
  }
  long long calc(string s) {
    long long answer1 = 0;
    for (int(i) = (0); (i) < (30); ++(i)) {
      answer1 = answer1 + root[i].findOccurance(s);
    }
    return answer1;
  }
};
int main() {
  int m;
  scanf("%d", &m);
  blocks b1;
  blocks b2;
  while (m--) {
    int type;
    scanf("%d", &type);
    string s;
    cin >> s;
    if (type == 1) {
      b1.insert(s);
    } else if (type == 2) {
      b2.insert(s);
    } else {
      long long answer1 = b1.calc(s) - b2.calc(s);
      printf("%lld\n", answer1);
      fflush(stdout);
    }
  }
  return 0;
}
