一、什么是Trie树
使用多个字符串构建一个Trie树:
8个字符串:
\(abcdef\) \(abdef\) \(aced\) \(bcdf\) \(bcff\) \(cdaa\) \(abc\) \(bcdc\)
总结
- \(son[N][26]\)来描述\(Trie\)树,第一维是构建\(Trie\)树的字符串个数乘以字符串的最大长度,第二维是指\(a\sim z\)共26种可能。
- \(son[i][j]=k\)是指节点\(i\)通过\(j\)这条边(比如\(a\),就是\(j=0\),\(b\)就是\(j=1\)...),值\(k\)是指到达了哪个子节点。
二、Trie树的构建与分析
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int trie[N][26];
int st[N];
int idx;
// 插入
void insert(string str) {
int p = 0;
for (int i = 0; i < str.size(); i++) {
int u = str[i] - 'a';
if (!trie[p][u]) trie[p][u] = ++idx;
p = trie[p][u];
}
st[p]++;
}
/**
测试用例:
8
abcdef
abdef
aced
bcdf
bcff
cdaa
abc
bcdc
*/
int main() {
int n;
cin >> n;
while (n--) {
//输入的字符串
string str;
cin >> str;
//插入到Trie树
insert(str);
}
printf("节点个数:%d\n", idx);
for (int i = 0; i <= idx; i++)
for (int j = 0; j < 26; j++)
if (trie[i][j])
printf("节点%d-> %c边 -> 节点%d\n", i, 'a' + j, trie[i][j]);
return 0;
}
三、本题答案
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int trie[N][26];
int st[N];
int idx;
// 插入
void insert(string str) {
int p = 0;
//遍历每一个字符串中的字符
for (int i = 0; i < str.size(); i++) {
//要放str[i],转化为u,可以理解u也就是str[i],即要放入的字符是什么
int u = str[i] - 'a';
//如果这条路径不存在,需要创建出来,这个位置有一个节点被创建,节点号为++idx
if (!trie[p][u]) trie[p][u] = ++idx;
//如果路径不存在,现在已经创建出来了;就是现在不管以前是不是路径存在,
//现在都有了这条路径,走进去,准备接收下一个字符
p = trie[p][u];
}
//记录p为终点的字符个数,有时也写为st[p]=1
st[p]++;
}
//Trie树查询
int query(string str) {
int p = 0;
for (int i = 0; i < str.size(); i++) {
int u = str[i] - 'a';
if (!trie[p][u]) return 0;//中间找不到结点就停止
p = trie[p][u];//继续深入查找~
}
//以p为结尾的单词的个数
return st[p];
}
int main() {
//优化输入
ios::sync_with_stdio(false);
int n;
cin >> n;
while (n--) {
char op;
//输入的字符串
string str;
cin >> op >> str;
//插入到Trie树
if (op == 'I') insert(str);
//表示x在集合中出现的次数
else printf("%d\n", query(str));
}
return 0;
}