当前位置: 首页 > news >正文

算法题(194):字典树

审题:
本题需要我们对多组数据进行前缀查询,然后将查询到的数量打印出来

思路:
由于本题需要输出前缀查询书,故本题需要使用到字典树

补充数据结构:字典树

字典树是一种用于处理字符串相关问题的数据结构

用处:

1.查询字符串是否出现,出现过多少次

2.查询有多少个字符串是以字符串为前缀

假设我们需要将“abc”,"ab","abcd"“ac”放入字典树里面

图示:

我们用树的边来表示字符串的字符,一整条路径的字符放在一起就表示一个字符串

插入逻辑:从根节点开始,若存在该字符的边就继续往下走,如果不存在就开一个新的节点,并在边上存储新的字符

疑问:此时记录了“abcd”字符串和“ab”字符串,我们要如何表示出插入了“ab”?

因为“ab”的插入路径和“abcd”的路径是重叠的,所以我们需要用一个end数组负责记录结束在某个节点的字符串的个数

疑问:如果需要查看以某个字符串为前缀的字符串有多少个,如何维护?

我们需要使用pass数组负责记录该节点被经过了多少次,只需要读取该节点就知道以当前字符串为前缀的字符串有多少个


方法一:字典树

我们先将所有字符串都存储进字典树中,然后进行前缀字符串个数查询,最后输出结果

难点1:字符的映射如何映射?

因为本题的字符类型不仅有小写字母,还有大写字母和数字字符,所以我们不能只开辟26个位置给每一条边用。所有本题出现的字符类型是26+26+10.所以我们需要开辟62个位置。

然后将小写字母映射到0~25,大写字母映射到26~51,数字字符映射到52~61.

难点2:如何清空残留数据?

如果直接使用memset来清空数组数据会导致超时,因为总共的字符数可能有3e6,然后存储的字符类型又有62,如果全部遍历一次清空数据,那么他们乘起来的结果是1e8级别,会导致超时

那么我们其实可以只将使用过的tr数组数据清除,因为idx表示最后一个使用到的节点的索引,我们只要将idx个节点的数据清空即可

解题:

(1)变量创建
 

#include<iostream>
#include<cstring>
using namespace std;
const int N = 3e6 + 10;
int t,n, q;
int tr[N][62], p[N];
int idx;

tr数组:字典树,行的数量取决于所有字符串总共的字符个数,列的数量取决于需要存储进字典树的字符类型个数

行表示节点,列表示边的字符类型

p数组:用于记录各个节点被经过的次数,用于提取所需结果

idx变量:表示总共的节点数,可以用于进行时间复杂度较低的清空残留数据操作

(2)主函数逻辑

int main()
{cin >> t;while (t--){//清空残留数据for (int i = 0; i <= idx; i++){p[i] = 0;for (int j = 0; j < 62; j++ ){tr[i][j] = 0;}}idx = 0;cin >> n >> q;for (int i = 1; i <= n; i++)//数据录入字典树{string s;cin >> s;insert(s);}for (int i = 1; i <= q; i++)//进行字符前缀查询{string s;cin >> s;cout << find_prv(s) << endl;}}return 0;
}

(3)字典树构建

void insert(string& s)
{int cur = 0;p[cur]++;for (auto ch : s){int path = get_num(ch);if (tr[cur][path] == 0) tr[cur][path] = ++idx;cur = tr[cur][path];p[cur]++;}
}

构建的时候要注意将p数组维护好,只要表示经过该节点的时候就进行p[cur]++操作

逻辑:从根节点开始进行字符串插入

对字符串字符依次进行:

将对应字符转换成映射值后,判断当前是否已经创建了该字符的路径

若创建了就更新cur指针为该路径的下一个节点

若没有创建就创建一个新节点,即将该节点以path类型的边链接新的节点

(4)查找函数

int find_prv(string& s)
{int cur = 0;for (auto ch : s){int path = get_num(ch);if (tr[cur][path] == 0) return 0;cur = tr[cur][path];}return p[cur];
}

总体逻辑和插入差不多,只有几个区别

(1)途中不用改变p数组的值

(2)当发现路径无法进行下去(tr存储的值为0),直接返回0,表示没有该前缀

(3)找到后返回当前节点的p数组值,他表示经过该目标节点的次数

(5)映射函数

int get_num(char& c)
{if (c >= 'a' && c <= 'z'){return c - 'a';}else if (c >= 'A' && c <= 'Z'){return c - 'A' + 26;}else{return c - '0' + 52;}
}

P8306 【模板】字典树 - 洛谷

http://www.dtcms.com/a/357366.html

相关文章:

  • 分享一些关于电商商品详情API接口的实际案例
  • 做视频孪生的公司哪家好,推荐一家优秀的视频孪生公司
  • 基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信
  • 「LangChain 学习笔记」LangChain大模型应用开发:代理 (Agent)
  • 【基础知识】互斥锁、读写锁、自旋锁的区别
  • 预制菜餐厅:工业化与温度餐平衡术
  • 软件测试(四):等价类和判定表
  • AI Agent(人工智能代理)当前人工智能领域最炙手可热的概念之一,需要你来了解
  • Flowchart 教程文档
  • 程序员之电工基础-CV程序解决目标检测
  • Dify 从入门到精通(第 63/100 篇):Dify 的多语言支持(进阶篇)
  • 基于MATLAB的三维TDOA定位算法仿真实现
  • Rspack
  • 软件安装教程(二):Pycharm安装与配置(Windows)
  • Redis与MySQL数据不一致问题
  • python 转偶数
  • 【开题答辩全过程】以 基于JSP的养生网站系统为例,包含答辩的问题和答案
  • vue3的pinia
  • 基于51单片机的简易逻辑分析仪设计
  • C# NET5.0及以上版本中如何处理MySQL大数据查询
  • 49_基于深度学习的课堂行为检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 微功耗遥测终端机在城市管网压力/流量监测中的应用
  • Ventoy中文版:新一代多系统启动U盘解决方案
  • 【线性代数入门 | 那忘算8】洛谷P3389 高斯消元(内附行列式教学)
  • t-sql和sql的有哪些区别和联系
  • Linux中的IP命令详解
  • uac播放与录制
  • 音乐云测试报告
  • JavaSE-什么是语法糖
  • 入职体检肌酐偏高 尿蛋白偏高