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

有什麼問題請不要忘記留言!!!!






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