#include <bits/stdc++.h>
#define ASCIIMAX 30
#define int long long int
#define double long double
#define cin fin
#define cout fout
using namespace std;
ifstream fin("trie.in");
ofstream fout("trie.out");
class Trie
{
const int NMAX;
vector<int> v;
Trie** sons;
int cate;
static int TestValidity(Trie* tmp)
{
bool ok=0;
int i;
for(i=0;i<tmp->NMAX;i++)
if( tmp->sons[i])
{ok=1;break;}
return ok;
}
public:
Trie():NMAX(ASCIIMAX), cate(0)
{
v.resize(NMAX);
sons= new Trie*[NMAX];
int i;
for(i=0;i<NMAX;i++)
sons[i]=NULL;
}
friend void AddAparition(Trie * tmp,const char* p)
{
if(*(p)==NULL)
{
tmp->cate++;
}
else
{
if( tmp->sons[ p[0]-'a' ]==0)
tmp->sons[p[0]-'a']= new Trie;
AddAparition(tmp->sons[p[0]-'a'],p+1);
}
}
friend int HowMany( Trie* tmp, const char* p)
{
if(*(p)==NULL)
{
return tmp->cate;
}
else
{
if( tmp->sons[ p[0]-'a' ]==0)
return 0;
return HowMany(tmp->sons[p[0]-'a'],p+1);
}
}
friend int DecAparition(Trie* tmp,const char* p)
{
if(*(p)==NULL)
{
if( tmp->cate!=0)
tmp->cate--;
///analizam daca acest nod are rost sa existe sau nu
return TestValidity(tmp);
}
else
{
if( tmp->sons[ p[0]-'a' ]!=0)
{
int value=DecAparition(tmp->sons[ p[0]-'a' ], p+1);
if( !value)
{
delete tmp->sons[ p[0]-'a' ];
tmp->sons[ p[0]-'a' ]=NULL;
}
}
return TestValidity(tmp);
}
}
friend int LongestPrefix(Trie* tmp, const char* p)
{
if( *(p)==NULL)
return 0;
if( tmp->sons[p[0]-'a'] )
{
return 1+ LongestPrefix(tmp-> sons[p[0]-'a'],p+1);
}
else
return 0;
}
};
int32_t main()
{
const int MAX_LENGTH=25;
Trie* root=new Trie;
int cer;
char string_to_add[MAX_LENGTH];
while(cin>>cer)
{
cin>>string_to_add;
switch (cer)
{
case 0:
AddAparition(root,string_to_add);
break;
case 1:
DecAparition(root, string_to_add);
break;
case 2:
cout<<HowMany(root, string_to_add)<<'\n';
break;
default:
cout<<LongestPrefix(root, string_to_add)<<'\n';
break;
}
}
return 0;
}