用數組實現字典樹


若要在n個字符串中查找某個字符串,如果使用暴力方法就需要逐個匹配字符串,複雜度是O(n * m),m是字符串平均長度。還有一種比較快的方法就是字典樹,像查單詞那樣,一次找一個字母,查找任意單詞,複雜度爲O(m),m是查詢或插入字符串的長度。

字典樹的基本性質:

  • 根結點不包含字符,其餘每結點都有一個字符
  • 從根結點到這一路徑末尾的所有點連起來就是該結點對應的字符串
  • 設置一個標誌標記末尾

常見的應用有字符串檢索、詞頻統計、字符串排序、前綴匹配。


Trie字符串統計

維護一個字符串集合,支持兩種操作:

  1. “I x”向集合中插入一個字符串x;
  2. “Q x”詢問一個字符串在集合中出現了多少次。
    共有N個操作,輸入的字符串總長度不超過 105,字符串僅包含小寫英文字母。

輸入格式

第一行包含整數N,表示操作數。

接下來N行,每行包含一個操作指令,指令爲”I x”或”Q x”中的一種。

輸出格式

對於每個詢問指令”Q x”,都要輸出一個整數作爲結果,表示x在集合中出現的次數。

每個結果佔一行。

數據範圍

1≤N≤2104

輸入樣例:

5
I abc
Q abc
Q ab
I ab
Q ab

輸出樣例:

1
0
1
#include<iostream>

using namespace std;

const int N = 100010;
int son[N][26], cnt[N], idx;//son存當前點的所有兒子,cnt表示以當前點結尾的單詞的個數,idx指向當前結點 
//下標是0 的點,既是根結點,又是空結點 
char str[N];
void insert(char str[])
{
	int p = 0;//根結點 
	for(int i = 0;str[i];i ++ )
	{
		int u = str[i] - 'a';//把a~z映射成0~25的編號 
		if(!son[p][u]) son[p][u] = ++ idx;//當前結點沒有兒子就要建一條路 
		p = son[p][u];//else  p表示當前分支的最後一個點 
	}	
	cnt [p] ++ ;//表示以最後這個點結尾的單詞數量又多了一個 
} 
//查找 
int query(char str[])//返回字符串出現多少次 
{
	int p = 0;
	for(int i =0; str[i]; i ++ )
	{
		int u = str[i] - 'a';
		if(!son[p][u]) return 0;//如果不存在當前結點說明不存在這個單詞 
		p = son[p][u];//else 繼續 
	}	
	
	return cnt[p];//以p結尾的單詞數量,在插入操作時值已經確定 
} 
int main() 
{
	int n;
	cin >> n;
	while(n -- )
	{
		char op[2];
		cin >> op >> str;
		if(op[0] == 'I') insert(str);
		else cout << query(str) << endl; 
	}	
	
	return 0;
}

HUD1251 統計難題

Problem Description

Ignatius最近遇到一個難題,老師交給他很多單詞(只有小寫字母組成,不會有重複的單詞出現),現在老師要他統計出以某個字符串爲前綴的單詞數量(單詞本身也是自己的前綴).

Input

輸入數據的第一部分是一張單詞表,每行一個單詞,單詞的長度不超過10,它們代表的是老師交給Ignatius統計的單詞,一個空行代表單詞表的結束.第二部分是一連串的提問,每行一個提問,每個提問都是一個字符串.

注意:本題只有一組測試數據,處理到文件結束.

Output

對於每個提問,給出以該字符串爲前綴的單詞的數量.

Sample Input

banana
band
bee
absolute
acm

ba
b
band
abc
 

Sample Output

2
3
1
0

數組實現:

#include<iostream>
#include<cstring>
using namespace std;

const int N = 5e5 + 5;
int son[N][26], cnt[N], idx;//son存當前點的所有兒子,cnt表示以當前點結尾的單詞的個數,idx指向當前結點 
//下標是0 的點,既是根結點,又是空結點 
char str[N];
void insert(char str[])
{
	int p = 0;//根結點 
	for(int i = 0;str[i];i ++ )
	{
		int u = str[i] - 'a';//把a~z映射成0~25的編號 
		if(!son[p][u]) son[p][u] = ++ idx;////當前新分配的存儲位置  
		p = son[p][u];//else  p表示當前分支的最後一個點
		cnt [p] ++ ;//只統計到前綴的
	}	

} 
//查找 
int query(char str[])//返回字符串出現多少次 
{
	int p = 0;
	for(int i =0; str[i]; i ++ )
	{
		int u = str[i] - 'a';
		if(!son[p][u]) return 0;//如果不存在當前結點說明不存在這個單詞 
		p = son[p][u];//else 繼續 
	}	
	
	return cnt[p];//以p結尾的單詞數量,在插入操作時值已經確定 
} 
int main() 
{
    while(gets(str))
    {
    	if(str[0] == ' ' || strlen(str) == 0)
    		break;
    	insert(str);
	}
	while(~scanf("%s", str))
	{
		printf("%d\n", query(str));
	}
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章