算法学习笔记(17)——Trie
Trie
Trie(字典树)是一种用于实现字符串快速检索的多叉树结构。Trie 的每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到一个字符 c ,就沿着当前节点的 c 字符指针,走向该指针指向的节点。下面详细讨论 Trie 的基本操作过程:
- 初始化:一棵空 Trie 仅包含一个根结点,该点的字符指针均指向空。
- 插入:当需要插入一个字符串 S 时,我们令一个指针 P 起初指向根节点。然后,依次扫描 S 中的每个字符 c。
- 若 P 的 c 字符指针指向一个已经存在的节点 Q,则令 P=Q。
- 若 P 的 c 字符指针指向空,则新建一个节点 Q,令 P 的 c 字符指针指向 Q,然后令 P=Q。
- 当 S 中的字符扫描完毕时,在当前节点 P 上标记它是一个字符串的末尾。
- 检索:当需要检索一个字符串 S 在 Trie 中是否存在时,我们令一个指针 P 起初指向根结点,然后依次扫描 S 中的每个字符 c:
- 若 P 的 c 字符指针指向空,则说明 S 没有插入过 Trie,结束检索。
- 若 P 的 c 字符指针指向一个已经存在的节点 Q,则令 P=Q。
- 当 S 中的字符扫描完毕时,若当前节点 P 被标记为一个字符串的末尾,则说明 S 在 Trie 中存在,否则说明 S 没有被插入过 Trie。
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
char str[N]; // 保存待操作的字符串
int son[N][26]; // 二维数组模拟Trie树,每个节点有26个子节点,对应26个字母
int cnt[N]; // 保存以某编号节点结尾的字符串数量
int idx; // 保存节点的编号
void insert(char *str)
{
// 初始字符指针指向根节点
int p = 0;
// 依次扫描str中的每个字符
for (int i = 0; str[i]; i ++ ) {
// 将英文字母转换为0~26的整数
int u = str[i] - 'a';
// 如果该字符不存在,则新建一个节点
if (!son[p][u]) son[p][u] = ++ idx;
// 将字符指针指向新插入(检索)的字符
p = son[p][u];
}
// 以p指针指向的字符结尾的字符串数量加一
cnt[p] ++;
}
int query(char *str)
{
// 初始字符指针指向根节点
int p = 0;
// 依次扫描str中的每个字符
for (int i = 0; str[i]; i ++ ) {
// 将英文字母转换为0~26的整数
int u = str[i] - 'a';
// 如果该字符不存在,则代表没有插入过这个字符串,返回数量0
if (!son[p][u]) return 0;
// 将字符指针指向新检索到的字符
p = son[p][u];
}
// 返回以p指针指向的字符结尾的字符串数量
return cnt[p];
}
int main()
{
cin >> n;
while (n -- ) {
char op[2];
scanf("%s%s", op, str);
if (*op == 'I') insert(str);
else printf("%d\n", query(str));
}
return 0;
}