HDU 5687 Problem C (2016年百度之星资格赛C题)

Problem C

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 625    Accepted Submission(s): 214


Problem Description
度熊手上有一本神奇的字典,你可以在它里面做如下三个操作:

  1、insert : 往神奇字典中插入一个单词

  2、delete: 在神奇字典中删除所有前缀等于给定字符串的单词

  3、search: 查询是否在神奇字典中有一个字符串的前缀等于给定的字符串
 

Input
这里仅有一组测试数据。第一行输入一个正整数N(1N100000),代表度熊对于字典的操作次数,接下来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

【分析】

       自己做这个题时,对字典树只是知道原理,定义,但不会应用,做题时直接拷贝的字典树模板,进行修改,但是没有修改出来,无法删除输入的删除字符串,所以一直不对,看了别人的代码才知道自己拷贝的模板只是一个介绍字典树的代码,应用于题目中太局限,看大神的代码看了半个多小时,还是感觉大神的世界好难搞懂~~~~委屈


【代码】

1. 绝对的大神

#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define maxn 3111111

struct node
{
    int num;        /* 表示某层某字母出现的次数 */
    int next[26];   // 数组座标保存字母,数组保存数据储存在第几层
    int fa;         /* 存储上一层,用于删除时的操作*/
} tree[maxn];
/*
 * num记录着每层以当前字符串为前缀的字符串的数目,插入时如果出现就++,
删除时就减去删除以删除字符串为前缀的数目,这样只需判断当前层num是否为0就能判断是否有此为前缀的字符串
 * fa用来保存当前字符串的上一层层数,用来往上返回,用来删除以输入字符串为前缀的字符串的数目
*/
char op[33], a[33];
int root, cnt;

// 函数功能:树的初始化
void New (int c, int fa)
{
    tree[c].num = 0;
    tree[c].fa = fa;
    memset (tree[c].next, -1, sizeof tree[c].next);
    return ;
}

// 函数功能:插入输入的字符串到树中
void Insert ()
{
    int l = strlen (a);
    int p = root;
    for (int i = 0; i < l; i++)
    {
        int id = a[i]-'a';
        if (tree[p].next[id] == -1)
        {
            tree[p].next[id] = ++cnt; // 新建的数据属于第几层
            New (cnt, p);
        }
        p = tree[p].next[id];  // 指向树的下一层
        tree[p].num++;    // 记录以这个字母往上所以的字符串的个数
    }
    return ;
}

// 函数功能:查询树中是否含有输入的字符串
int Search ()
{
    int l = strlen (a);
    int p = root;
    for (int i = 0; i < l; i++)
    {
        int id = a[i]-'a';
        if (tree[p].next[id] == -1)
        {
            return 0;
        }
        p = tree[p].next[id];
    }
    return tree[p].num;
}
/*((((重点))))*/
// 函数功能:删除输入的字符串
/*
 *       题意是删除以输入字符串为前缀的字符串,但代码并没有删除,只是将其覆盖了,
 * 用num存储以当前层数组成的字符串为前缀的字符串的个数,减去输入字符串为前缀的数目,
 * 就等价于删除字符串,这样更加节约时间,不用删了再建,建了再删
*/
void Delete ()
{
    int l = strlen (a);
    int p = root;
    for (int i = 0; i < l; i++) // 遍历到删除字符串的末尾
    {
        int id = a[i]-'a';
        if (tree[p].next[id] == -1)
        {
            return ;
        }
        p = tree[p].next[id];
    }
    int ans = tree[p].num;   // 以删除字符串为前缀的字符串数目
    while (tree[p].fa != -1)  /* 一直返回到头结点 */
    {
        tree[p].num -= ans;   /* 往上遍历减去以删除字符串为前缀的字符串的数目 */
        /* 不会出现负值,因为越往上num值越大,num存储以当前层数往上的字符串为前缀的字符串的数目 */
        p = tree[p].fa;
    }
    p = root;
    for (int i = 0; i < l; i++)
    {
        int id = a[i]-'a';
        if (tree[tree[p].next[id]].num == 0) // 如果某层没有以删除字符串为前缀的字符串时就直接跳出就行了
        {
            tree[p].next[id] = -1;
            return ;
        }
        p = tree[p].next[id];
    }
    return ;
}

int main ()
{
    //freopen ("in.txt", "r", stdin);
    int n;
    scanf ("%d", &n);
    root = cnt = 0;
    New (root, -1);
    while (n--)
    {
        scanf ("%s%s", op, a); // 输入字符串
        if (op[0] == 'i') // 插入
        {
            Insert ();
        }
        else if (op[0] == 'd') // 删除
        {
            Delete ();
        }
        else if (op[0] == 's') // 查询
        {
            int ans = Search ();
            printf ("%s\n", ans ? "Yes" : "No");
        }
    }
    return 0;
}

看完瞬间长知识!


2.一般人想到的方法

#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
struct note
{
    int next[26];
    int v;   /* 表示以该字符串为前缀的字符串数目*/
    void init()
    {
        v=0;
        memset(next,-1,sizeof(next));
    }
};
note l[1200500];
int tot=0;
void add(char s[],int len)
{
    int now=0;
    for(int i=0; i<len; i++)
    {
        //   cout<<s<<":"<<now<<endl;
        int tmp = s[i]-'a';
        if(l[now].next[tmp]==-1)
        {
            int nn=tot++;
            l[nn].init();
            l[now].next[tmp]=nn;
        }
        now=l[now].next[tmp];
        l[now].v+=1;
    }
}
int query(char s[],int len)
{
    int now=0;
    for(int i=0; i<len; i++)
    {
        int tmp =s[i]-'a';
        if(l[now].next[tmp]==-1)
            return 0;
        now =l[now].next[tmp];
    }
    return l[now].v;
}
void delet(char s[],int len,int cnt)
{
    int now=0;
    for(int i=0; i<len; i++)
    {
        int tmp=s[i]-'a';
        if(l[now].next[tmp]==-1)
            return ;
        l[now].v-=cnt;
        now =l[now].next[tmp];
    }
    l[now].v=0;  /*将以删除字符串为前缀的字符串的数目置0*/
    for(int i=0; i<26; i++) /* 将以删除字符串为前缀的字符串下面的元素删除*/
        l[now].next[i]=-1;
}
char ss[100],cc[100];
int n;
int main()
{
    // freopen("in.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0; i<1200500; i++)
            l[i].init();
        tot=1;
        for(int i=0; i<n; i++)
        {
            scanf("%s%s",ss,cc);
            if(ss[0]=='i')
            {
                add(cc,strlen(cc));
            }
            else if(ss[0]=='s')
            {
                int ans=query(cc,strlen(cc));
                if(ans) printf("Yes\n");
                else  printf("No\n");
            }
            else
            {
                int cnt=query(cc,strlen(cc));
                delet(cc,strlen(cc),cnt);
            }
        }
    }
}

有什么问题请不要忘记留言!!!!






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章