题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5687

此题比起经典的字典树问题多了一个删除的选项,关于删除选项,我们只需要记录每一位的之前前缀出现的次数,当我们删除的前缀出现时,我们把这个要删除的后面的所有子节点删掉,然后前面每一位的出现次数减去前缀出现的次数。为什么要这么做呢,因为我们是要删除所有以这个为前缀的单词,所以在删除后缀的时候,我们有可能也会删掉前缀,比如insert aaaa delete aaa search aa,这组数据我们在删除aaa后,所有的都没了,再比如insert aaab insert aab delete aaa search aa,这组我们只会删掉aaab这个单词,很明显我们可以通过出现的次数来判断是否可以删掉前缀前面的节点。


#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 1e5 + 5;
char oper[10];
char str[35];
const int NODE = N * 30;
int tree[NODE][26], val[NODE];
int sz;
void clear()
{
memset(tree[0],0,sizeof (tree[0]));
sz = 1;
}
int idx(char c)
{
return c - 'a';
}
void insertTrie(char *s)
{
int u = 0;
for(int c, i=0; s[i]; ++i)
{
c = idx(s[i]);
if(!tree[u][c])
{
memset(tree[sz],0,sizeof(tree[sz]));
val[sz] = 0;
tree[u][c] = sz++;
}
u = tree[u][c];
val[u]++;
}
}
void deleteTrie(char *s, int num)
{
int u = 0;
for(int c, i=0; s[i]; ++i)
{
c = idx(s[i]);
u = tree[u][c];
val[u] -= num;
}
memset(tree[u], 0, sizeof(tree[u]));
}
int searchTrie(char *t)
{
int u = 0;
for(int c, i=0; t[i]; ++i)
{
c = idx(t[i]);
if(!tree[u][c]) return 0;
u = tree[u][c];
}
return val[u];
}
int main()
{
int n;
while(scanf ("%d", &n) == 1)
{
clear();
for(int i=0; i<n; ++i)
{
scanf ("%s%s", oper, str);
if(oper[0] == 'i')
insertTrie(str);
else if(oper[0] == 'd')
{
int cnt = searchTrie(str);
if(cnt > 0)
deleteTrie(str, cnt);
}
else
{
int cnt = searchTrie(str);
if(cnt > 0)
puts("Yes");
else
puts("No");
}
}
}
return 0;
}