Trie树简介及其应用

 写在前面:大家好!我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭!
用知识改变命运,用知识成就未来!加油 (ง •̀o•́)ง (ง •̀o•́)ง

Trie树简介

Trie树又称 字典树、前缀树、单词查找树 等等。是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

性质

  1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
  2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
  3. 每个节点的所有子节点包含的字符都不相同。

优点

 可以最大限度地减少无谓的字符串比较,故可以用于词频统计和大量字符串排序。自带排序功能(类似Radix Sort),中序遍历 Trie树 可以得到排序。

缺点

 空间复杂度较大,Trie树其实是一个以空间换时间的算法。可以用链表来动态开辟空间,达到空间上利用率的最大化。

应用

串的快速检索

 给出 N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

 先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。

“串”排序

 给定 N 个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出。

 用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。

最长公共前缀

 对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。

模板题

题目来源:AcWing 835. Trie字符串统计

题目信息

题目描述

 维护一个字符串集合,支持两种操作:

  1. “I x”向集合中插入一个字符串x;
  2. “Q x”询问一个字符串在集合中出现了多少次。

 共有 N 个操作,输入的字符串总长度不超过 1 0 5 10^5 105,字符串仅包含小写英文字母。

输入格式

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

 接下来 N 行,每行包含一个操作指令,指令为 ”I x””Q x” 中的一种。

输出格式

 对于每个询问指令 ”Q x”,都要输出一个整数作为结果,表示 x 在集合中出现的次数。

 每个结果占一行。

数据范围

1 ≤ N ≤ 2 ∗ ∗ 1 0 4 10^4 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;  // 下标是 0 的节点既是根节点,又是空节点
/* 
1、对 son[][] 的说明;
    son[][] 存储的是所有节点的儿子节点,
    其中第一维存放的是根节点的坐标,第二维存放的是当前根中存放的值,
    将字母 a - z 映射成数字 0 - 25。二维数组的值存放的是当前节点所有的
    子节点。该数值即当前根节点的所有子节点的一维坐标,第二维即子节点的值。
    
2、cnt[]:
    表示以当前点为结尾的字符串有多少个
    
3、idx: 表示当前用到的下标
*/
char str[N];

void insert(char str[]) {
    int p = 0; // 从根节点开始遍历
    for (int i = 0; str[i]; i++) {
        int u = str[i] - 'a';    // 将当前节点对应的子节点的编号映射出来
        if (!son[p][u]) son[p][u] = ++idx; // 如果没有节点则创建该节点
        p = son[p][u]; // 走到下一个节点
    }
    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]; // 如果有则遍历下一个节点,判断下一个节点是否存在
    }
    return cnt[p]; 
    /* 如果当前查找的字符串,在树中存在,但是树中没有以该节点结尾的单词,那么
    当前节点的存储的任然是 0 , 因为在插入的过程中该点没有结束标记。
    (这里的结束标记指的是插入的最后一步:cnt[p]++; // 将以该节点结尾的字符串的数量加一)
    */
}

int main() {
    int n;
    cin >> n;
    while (n--) {
        char op[2];
        scanf("%s%s", op, str);
        if (op[0] == 'I') insert(str);
        else cout << query(str) << endl;
    }
    return 0;
}

未完待续,持续更新中……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴空๓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值