2016百度之星资格赛C

题目链接: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的话说明之前这个位置是断开的,就将当前节点之后所连接的节点次数清空。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值