若要在n個字符串中查找某個字符串,如果使用暴力方法就需要逐個匹配字符串,複雜度是O(n * m),m是字符串平均長度。還有一種比較快的方法就是字典樹,像查單詞那樣,一次找一個字母,查找任意單詞,複雜度爲O(m),m是查詢或插入字符串的長度。
字典樹的基本性質:
- 根結點不包含字符,其餘每結點都有一個字符
- 從根結點到這一路徑末尾的所有點連起來就是該結點對應的字符串
- 設置一個標誌標記末尾
常見的應用有字符串檢索、詞頻統計、字符串排序、前綴匹配。
Trie字符串統計
維護一個字符串集合,支持兩種操作:
- “I x”向集合中插入一個字符串x;
- “Q x”詢問一個字符串在集合中出現了多少次。
共有N個操作,輸入的字符串總長度不超過 105,字符串僅包含小寫英文字母。
輸入格式
第一行包含整數N,表示操作數。
接下來N行,每行包含一個操作指令,指令爲”I x”或”Q x”中的一種。
輸出格式
對於每個詢問指令”Q x”,都要輸出一個整數作爲結果,表示x在集合中出現的次數。
每個結果佔一行。
數據範圍
1≤N≤2∗104
輸入樣例:
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;
}