Problem C
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 2772 Accepted Submission(s): 768
Problem Description
度熊手上有一本神奇的字典,你可以在它里面做如下三个操作:
1、insert : 往神奇字典中插入一个单词
2、delete: 在神奇字典中删除所有前缀等于给定字符串的单词
3、search: 查询是否在神奇字典中有一个字符串的前缀等于给定的字符串
Input
这里仅有一组测试数据。第一行输入一个正整数N(1≤N≤100000),代表度熊对于字典的操作次数,接下来N行,每行包含两个字符串,中间中用空格隔开。第一个字符串代表了相关的操作(包括: insert, delete 或者 search)。第二个字符串代表了相关操作后指定的那个字符串,第二个字符串的长度不会超过30。第二个字符串仅由小写字母组成。
Output
对于每一个search 操作,如果在度熊的字典中存在给定的字符串为前缀的单词,则输出Yes 否则输出 No。
Sample Input
5
insert hello
insert hehe
search h
delete he
search hello
Sample Output
Yes
No
Source
2016"百度之星" - 资格赛(Astar Round1)
问题链接:HDU5687 Problem C
解题思路:问题的关键是如何实现delete操作。sum[]存储前缀出现的次数,在进行delete操作时,先找到最后一个结点对应的sum值,假定为cnt,然后再将整个操作路径上的结点的sum值-cnt。减cnt是由于以s为前缀的字符串有个数,删除它们自然也会让路径上的sum值减cnt,最后还要将最后结点的儿子结点即 trie[rt][i] 置为0,这是由于要删除这些字符串,那么它们对应的结点也没有了。
AC的C++程序:
#include<iostream>
#include<cstring>
using namespace std;
const int N=1200600;
int trie[N][26],sum[N],tot;//sum存储前缀出现的次数
void insert(char *s)
{
int rt=0;
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(!trie[rt][x])
trie[rt][x]=++tot;
rt=trie[rt][x];
sum[rt]++;
}
}
int search(char *s)
{
int rt=0;
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(!trie[rt][x])
return 0;
rt=trie[rt][x];
}
return sum[rt];//返回前缀s出现的次数
}
void delete_tree(char *s)
{
int rt=0;
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(!trie[rt][x])
return;
rt=trie[rt][x];
}
//修改路径上个结点的sum值
int cnt=sum[rt];
rt=0;
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
rt=trie[rt][x];
sum[rt]-=cnt;//减去一s为前缀的字符串的数量
}
//删除儿子结点
for(int i=0;i<26;i++)
trie[rt][i]=0;
}
int main()
{
int n;
char d[10],s[35];
scanf("%d",&n);
while(n--)
{
scanf("%s%s",d,s);
if(d[0]=='i')
insert(s);
else if(d[0]=='d')
delete_tree(s);
else if(d[0]=='s')
printf("%s\n",search(s)!=0?"Yes":"No");
}
return 0;
}