题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5687
题意:
度熊手上有一本神奇的字典,你可以在它里面做如下三个操作:
1、insert : 往神奇字典中插入一个单词
2、delete: 在神奇字典中删除所有前缀等于给定字符串的单词
3、search: 查询是否在神奇字典中有一个字符串的前缀等于给定的字符串
思路:用trie树来做。我们维护每个节点的经过次数。
那么我们需要一个查询函数,查询一个单词结尾节点的次数。一个删除函数,删除一个单词m次(每个节点减m)。一个插入函数。
插入单词时,到达一个节点,该节点的次数+1;删除单词时,先判断删除的这个前缀在当前字典树里是否存在,如果存在的话,得到这个单词结尾节点的次数m,然后用删除函数将路径上的每个节点都减去m。
注意一种情况,插入 abcde 删除abc 插入 abc 查询 abcd ,这时候答案应该是no,因为只有abc存在,但是如果插入单词时不注意处理的话就导致程序变成yes,因为de节点在删除的时候没有处理,所以删除的时候只是断开了前后,但是一旦路径恢复,会导致这个单词重新出现。所以解决方案是,当插入单词是,判断当前节点的次数是不是1,如果是1的话说明之前这个位置是断开的,就将当前节点之后所连接的节点次数清空。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
const int maxnode = 700000;
const int sz = 26;
struct trie
{
int next[maxnode][sz];
int num[maxnode];
int size; // 当前大小
trie()
{
size = 0;
memset(next,0,sizeof(next));
memset(num,0,sizeof(num));
}
void insert(char s[]) //插入一个字符串
{
int len = strlen(s);
int now = 0;
for( int i = 0; i < len; i++ )
{
int index = s[i] - 'a'; //当前字符对应的下一个节点的下标
if ( !next[now][index] )
{
next[now][index] = ++size; //新开一个节点
}
now = next[now][index];
num[now]++;
if( num[now] == 1 )//当前节点是新节点(可能以前也存在,但是被删除过了)
{
rep(j,0,25) //防止之前删除操作造成的影响
if( next[now][j] ) num[next[now][j]] = 0;
}
}
}
void Delete(char s[],int m)
{
int len = strlen(s);
int now = 0;
for( int i = 0; i < len; i++ )
{
int index = s[i] - 'a';
now = next[now][index];
num[now] -= m;
if( num[now]<0 ) num[now] = 0;
}
}
int find(char s[]) //查找是否含有这个字符串,返回出现次数
{
int now = 0;
int len = strlen(s);
for(int i = 0; i < len; i++)
{
int index = s[i] - 'a';
if ( !next[now][index] ) return 0;
now = next[now][index];
if ( num[now] <= 0 ) return 0;
}
return num[now];
}
};
int n;
trie x;
char op[10];
char str[50];
int main()
{
scanf("%d",&n);
rep(i,1,n)
{
getchar();
scanf("%s %s",op,str);
if ( op[0] == 'i' )
{
x.insert(str);
}
else if ( op[0] == 's' )
{
puts( ( x.find(str) )>0?"Yes":"No");
}
else if ( op[0] == 'd' )
{
int k;
if ( ( k = x.find(str) ) )
x.Delete(str,k);
}
}
return 0;
}
思路:用trie树来做。我们维护每个节点的经过次数。
那么我们需要一个查询函数,查询一个单词结尾节点的次数。一个删除函数,删除一个单词m次(每个节点减m)。一个插入函数。
插入单词时,到达一个节点,该节点的次数+1;删除单词时,先判断删除的这个前缀在当前字典树里是否存在,如果存在的话,得到这个单词结尾节点的次数m,然后用删除函数将路径上的每个节点都减去m。
注意一种情况,插入 abcde 删除abc 插入 abc 查询 abcd ,这时候答案应该是no,因为只有abc存在,但是如果插入单词时不注意处理的话就导致程序变成yes,因为de节点在删除的时候没有处理,所以删除的时候只是断开了前后,但是一旦路径恢复,会导致这个单词重新出现。所以解决方案是,当插入单词是,判断当前节点的次数是不是1,如果是1的话说明之前这个位置是断开的,就将当前节点之后所连接的节点次数清空。