HDU5687 Problem C【字典树】

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
#include

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;
} 

 

你可能感兴趣的:(数据结构)