#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>
using namespace std;
ifstream in("trie.in");
ofstream out("trie.out");
#define ll long long
#define ull unsigned long long
#define ui unsigned int
#define pb push_back
const int NMax = 13 + 5;
int N;
struct nodTrie {
int nr,nrSub;
nodTrie *nxt[30];
nodTrie() {
nr = nrSub = 0;
for (int i=0;i<30;++i) {
nxt[i] = nullptr;
}
}
}root;
void update(nodTrie*,const string&,int,int);
int query(nodTrie*,const string&,int);
int querySub(nodTrie*,const string&,int);
int main() {
int tip;
string cuv;
in>>tip;
while (!in.fail()) {
in>>cuv;
switch (tip) {
case 2: {
out<<query(&root,cuv,0)<<'\n';
break;
}
case 3: {
out<<querySub(&root,cuv,0)<<'\n';
break;
}
default: {
int add = (tip == 0) ? +1 : -1;
update(&root,cuv,0,add);
}
}
in>>tip;
}
return 0;
}
#define urm (node -> nxt[cuv[idx]-'a'])
void update(nodTrie* node,const string& cuv,int idx,int add) {
node -> nrSub += add;
if (cuv.size() == idx) {
node -> nr += add;
return;
}
if (urm == 0) {
urm = new nodTrie;
}
update(urm,cuv,idx+1,add);
if (urm -> nrSub == 0) {
delete urm;
urm = 0;
}
}
int query(nodTrie* node,const string& cuv,int idx) {
if (cuv.size() == idx) {
return node -> nr;
}
if (urm == 0) {
return 0;
}
return query(urm,cuv,idx+1);
}
int querySub(nodTrie* node,const string& cuv,int idx) {
if (cuv.size() == idx || urm == 0) {
return idx;
}
return querySub(urm,cuv,idx+1);
}