/// left-child right-sibling tree, memorie alocata dinamic
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int LMAX = 20;
const int SIGMA = 3;
const int NROP = 100000;
struct nod {
char ch;
int nr_ap, capat;
nod *child, *sibling;
nod(char _ch = '*') : ch(_ch), nr_ap(0), capat(0), child(0), sibling(0) {}
};
void add_cuv(nod *root, char *cuv, int l) {
root->nr_ap++;
if(l == 0)
root->capat++;
else {
nod *last_child = 0;
for(nod *child = root->child; child; child = child->sibling) {
last_child = child;
if(child->ch == cuv[0]) {
add_cuv(child, cuv + 1, l - 1);
return;
}
}
if(!last_child) {
root->child = new nod(cuv[0]);
add_cuv(root->child, cuv + 1, l - 1);
}
else {
last_child->sibling = new nod(cuv[0]);
add_cuv(last_child->sibling, cuv + 1, l - 1);
}
}
}
void del_cuv(nod *root, char *cuv, int l) {
root->nr_ap--;
if(l == 0)
root->capat--;
else {
nod *prev = root;
for(nod *child = root->child; child; prev = child, child = child->sibling)
if(child->ch == cuv[0]) {
del_cuv(child, cuv + 1, l - 1);
if(child->nr_ap == 0) {
if(prev == root)
prev->child = child->sibling;
else
prev->sibling = child->sibling;
delete child;
}
return;
}
}
}
int get_nr_ap(nod *root, char *cuv, int l) {
if(l == 0)
return root->capat;
for(nod *child = root->child; child; child = child->sibling)
if(child->ch == cuv[0])
return get_nr_ap(child, cuv + 1, l - 1);
return 0;
}
int get_longest_prefix(nod *root, char *cuv, int l) {
if(l == 0)
return 0;
for(nod *child = root->child; child; child = child->sibling)
if(child->ch == cuv[0])
return 1 + get_longest_prefix(child, cuv + 1, l - 1);
return 0;
}
int main() {
freopen("trie.in", "r", stdin);
freopen("trie.out", "w", stdout);
int op;
char cuv[LMAX + 5];
nod *root = new nod;
while(scanf("%d %s ", &op, cuv) != EOF) {
if(op == 0)
add_cuv(root, cuv, strlen(cuv));
else if(op == 1)
del_cuv(root, cuv, strlen(cuv));
else if(op == 2)
printf("%d\n", get_nr_ap(root, cuv, strlen(cuv)));
else
printf("%d\n", get_longest_prefix(root, cuv, strlen(cuv)));
}
return 0;
}