簡單的描述就是一個字典樹, 我們用下圖來簡單描述一下。
上述的字典樹代表着
ab
abc
abk
cd
ca
b
這六個單詞,我們不難發現其中標記是紅色的代表從一個僞根節點到這是一個完整的單詞。不同的單詞有重複的部分,例如accepted,accept。
下面是建樹代碼。
其中tree[ i ][ j ]數組的意思是,以 i j 爲父節點的子節點的一個下標,另一個下標就是節點的value,通常就是字母的值。
const int N = 1e6 + 10;
int tree[N][26], tot;
bool is_word[N];
char str[N];
void build_tree() {
int root = 0;
int n = strlen(str);
for(int i = 0; i < n; i++) {
if(!tree[root][str[i] - 'a']) tree[root][str[i] - 'a'] = ++tot;
root = tree[root][str[i] - 'a'];
}
is_word[root] = true;//這個點結束的單詞的個數。
}
我們把上面的樹在數組中打印下來。
我們可以模擬一遍上面的數組到底是如何形成的,這樣有助於方便理解。
其實一開始我也搞不懂到底這樣爲什麼可以,後面自己模擬一下自然就懂了。
字典樹建好了,接下來就是查找字典裏有沒有出現過某個單詞了。
const int N = 1e6 + 10;
int tree[N][26], tot;
bool is_word[N];
char str[N];
bool find_word() {
int root = 0, ans = 0;
int n = strlen(str);
for(int i = 0; i < n; i++) {
root = tree[root][str[i] - 'a'];
if(!root) return false;//如果有一個字母是沒有出現的自然就是查找失敗了。
}
return is_word[root];//再次判斷,查找的單詞是否只是字典裏的一個單詞的字串。
}
一道水題
前綴統計
給定N個字符串S1,S2…SN,接下來進行M次詢問,每次詢問給定一個字符串T,求S1~SN中有多少個字符串是T的前綴。
輸入字符串的總長度不超過106,僅包含小寫字母。
輸入格式
第一行輸入兩個整數N,M。
接下來N行每行輸入一個字符串Si。
接下來M行每行一個字符串T用以詢問。
輸出格式
對於每個詢問,輸出一個整數表示答案。
每個答案佔一行。
輸入樣例:
3 2
ab
bc
abc
abc
efg
輸出樣例:
2
0
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1e6 + 10;
int tree[N][26], tot, num_tree[N];
char str[N];
void build_tree() {
int root = 0;
int n = strlen(str);
for(int i = 0; i < n; i++) {
if(!tree[root][str[i] - 'a']) tree[root][str[i] - 'a'] = ++tot;
root = tree[root][str[i] - 'a'];
}
num_tree[root]++;
}
int find_sub() {
int root = 0, ans = 0;
int n = strlen(str);
for(int i = 0; i < n; i++) {
root = tree[root][str[i] - 'a'];
if(!root) return ans;
ans += num_tree[root];
}
return ans;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++) {
scanf("%s", str);
build_tree();
}
for(int i = 0; i < m; i++) {
scanf("%s", str);
printf("%d\n", find_sub());
}
return 0;
}