Problem C
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 625 Accepted Submission(s): 214
1、insert : 往神奇字典中插入一個單詞
2、delete: 在神奇字典中刪除所有前綴等於給定字符串的單詞
3、search: 查詢是否在神奇字典中有一個字符串的前綴等於給定的字符串
【分析】
自己做這個題時,對字典樹只是知道原理,定義,但不會應用,做題時直接拷貝的字典樹模板,進行修改,但是沒有修改出來,無法刪除輸入的刪除字符串,所以一直不對,看了別人的代碼才知道自己拷貝的模板只是一個介紹字典樹的代碼,應用於題目中太侷限,看大神的代碼看了半個多小時,還是感覺大神的世界好難搞懂~~~~
【代碼】
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);
}
}
}
}
有什麼問題請不要忘記留言!!!!