字典树(trie树)详解

【本文概要】本文主要介绍了字典树的概念,字典树的一般算法,包括初始化,插入,查找等,最后举了比较典型的案例以及算法比赛中常见的“01树”来辅助理解字典树这种特殊的数据结构。

1、什么是字典树

        字典树,是一种特殊的树状数据结构,对于解决字符串相关问题非常有效。一般而言,我们认为字典树是一种前缀树,但有时它也可以是后缀树(具体见下图)。字典树在统计、保存大量字符串中有着极大的优势:它利用字符串的公共前缀或后缀来减少查询时间,最大限度地减少不必要的字符串比较,使得查询的效率比一般算法高得多。

        如上图所示,常见的字典树的每一个节点是由一个数据域(用来标记是否在此处有字符串终止)与一个长度为26的指针域(表示26个小写字母)组成。一般我们在根结点不存储任何数据,这样是为了可以存储所有的字符串,从根结点到某一个节点,路过的字符连起来就是该节点对应的字符串。由于每个节点的子节点字符不同,也就是说明找到对应单词、字符是唯一的。

2、字典树的实现

        这里我们整理字典树的定义、插入和查找的相应算法的写法。

2.1 字典树的定义

const int size = 26;
struct Node {bool k; // true表示有字符串在此结尾,false表示无字符串在此结尾Node* next[size];Node() :k(false) { // 给成员变量赋值falsefor (int i = 0; i < size; ++i) {next[i] = nullptr; // 初始化都是空指针}}
};

2.2 字典树的插入

void insert_ch(char *ch) {Node *p = head;for (int i = 0; ch[i]; ++i) {if (p->next[ch[i] - 'a'] == nullptr) // 判断下层节点是否存在p->next[ch[i] - 'a'] = new Node; // 开辟新空间p = p->next[ch[i] - 'a'];}p->k = true; // 进行字符串结尾标记
}

        每次从根节点进行插入,如果向下的节点已经存在,就直接读取,否则拓展一个新节点。之后将最后一个节点的k标记为true表示该位置有一个字符串结尾。

2.3 字典树的查找

bool find_ch(char *ch) {Node *p = head;for (int i = 0; ch[i]; ++i) {if (p->next[ch[i] - 'a'] == nullptr) { // 判断下层节点是否存在return false; // 不存在即判否}p = p->next[ch[i] - 'a'];}return p->k; // 最终判断
}

        基本过程与插入相同,向下查找,入过该节点不存在,直接返回false,如果存在一直向下查找,最终返回末尾标记的k。

3、关于字典树的常见问题整理

3.1 依依的瓶中信

//本题考察字典树的扩展应用
//其具体算法仍是字典树的插入与查询
//需要注意的是当前字符串不能与自己匹配,
//解决的方法是写一个删除函数,先将当前字符串删除再查询,查询后再恢复 
//由于插入与删除的本质相同,只是cnt数组对应位置的增加或减小,故只需改写插入函数即可 
#include <bits/stdc++.h>using namespace std;const int maxn=1e5+100;string str[maxn];//存储原始字符串组 
int nex[maxn][27];//nex[x][0]表示从第x个结点出发,边为'a'的下一个结点地址 
int cnt[maxn];//cnt[i]表示以第i个结点结尾的前缀的数量 
int idx=2;//用于动态开点 void Insert(string s,int tag)//将字符串s插入字典树中,或将其从字典树中删除
//若传入tag=1,则为插入;若传入tag=-1,则为删除
//插入与删除的本质是令对应的cnt[x]+1或-1 
{int x=1;//初始从根结点(1号)开始 for(int i=0;i<s.size();i++)//遍历字符串s {cnt[x]+=tag;//对每个字符,以该字符结尾的前缀数量均+1/-1 if(nex[x][s[i]-'a']==0)//若该字符(存储该字符的边)未被记录 {nex[x][s[i]-'a']=idx++;//则动态开点并记录之 }x=nex[x][s[i]-'a'];//继续向下追溯 }        cnt[x]+=tag;//结尾字符对应的前缀数量+1/-1 
}int Search(string s)//在字典树中查找与s最接近的字符串,并返回匹配的最长前缀的长度 
{int x=1;//初始从根结点(1号)开始 int ans=0;//记录匹配的最长前缀的长度 for(int i=0;i<s.size();i++)//遍历字符串 {if(nex[x][s[i]-'a']==0)//已经无法再匹配(不存在记录当前字符的边){return ans;//返回之前累计的长度 }    x=nex[x][s[i]-'a'];//若能继续匹配,则继续向下追溯 if(cnt[x]==0)return ans;//已经不存在以x结点结尾的前缀,返回之前累计的长度//注意以上这句不可省略,因为在删除操作中只是减少了字符串出现的次数,并没有删除之前记录的字符 ans++;//计数值加1,重复上述操作 }    return ans;//最终返回ans 
}int main()
{int N;cin>>N;for(int i=0;i<N;i++)//输入N个字符串 {cin>>str[i];Insert(str[i],1);//插入 }for(int i=0;i<N;i++)//N组查询 {Insert(str[i],-1);//先将当前字符串删除 cout<<Search(str[i])<<endl;//查询匹配的最长前缀的长度并输出 Insert(str[i],1);//将当前字符串重新插入以恢复字典树 }return 0;
}

3.2 XOR的最大值

这道题使用了字典树的特例——01树,这里简单做个介绍:

// 本题考察的是字典树的一个特例——01树
// 01树的一个常见用处就是用来查找异或最大值
// 由于查找过程类似二分,大大减少了所用的时间
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int son[32 * N][2], tot = 1; // 表示每个节点的子节点,节点编号// 在01树中插入新的数
void insert(int num) {int o = 1; // 从第一层开始插入,深度较小的节点存储数的高位for (int i = 30; i >= 0; --i) { // 这里的最高层数是一个估计值,大约30位int bit = num >> i & 1; // num的第i位if (!son[o][bit]) { // 如果这个子节点之前未被创建son[o][bit] = ++tot; // 那么就给这个新的子节点编上序号} // 这里节点覆盖不影响之后的查找,因为查找过程中只需要确定这个数存在即可,// 并不关注这个数的编号o = son[o][bit]; // 之后就从这个子节点所在序号处继续插入}
}// 在01树中查找异或最大值
int query(int num) {int o = 1, res = 0;for (int i = 30; i >= 0; --i) {int bit = num >> i & 1;if (son[o][!bit]) { // 如果该位的不同数节点存在o = son[o][!bit]; res |= 1ll << i; // 那么就证明存在一个数的该位可以通过异或产生有效位} // 这里利用了二进制的一个特殊之处在于第i位为1形成的数大于从第0位到第i-1位均为1形成的数else {o = son[o][bit];}}return res;
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n; cin >> n;for (int i = 1; i <= n; ++i) {int x; cin >> x;insert(x);}int q; cin >> q;while (q--) {int x; cin >> x;cout << query(x) << "\n";}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/29740.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【html期末作业网页设计】

html期末作业网页设计 作者有话说项目功能介绍 网站结构完整代码网站样图 作者有话说 目前&#xff0c;我们的项目已经搭建了各页面的基本框架&#xff0c;但内容填充还不完善&#xff0c;各页面之间的跳转逻辑也还需要进一步优化。 我们深知&#xff0c;一个好的项目需要不断…

数据安全VS创作自由:ChatGPT与国产AI工具隐私管理对比——论文党程序员必看的避坑指南

文章目录 数据安全VS创作自由&#xff1a;ChatGPT与国产AI工具隐私管理对比——论文党程序员必看的避坑指南ChatGPTKimi腾讯元宝DeepSeek 数据安全VS创作自由&#xff1a;ChatGPT与国产AI工具隐私管理对比——论文党程序员必看的避坑指南 产品隐私设置操作路径隐私协议ChatGPT…

C语言实现贪吃蛇

贪吃蛇小游戏的实现 讲解1.Win32 API介绍1.1控制台程序(system())1.2控制台屏幕上的坐标CDDRD1.3 GetStdHandle1.4 GetConsoleCursorInfo1.5 SetConsoleCursorInfo1.6 SetConsoleCursorPostion1.7 GetAsyncKeyState 2.游戏设计2.1地图2.2蛇身和食物2.3数据结构设计2.4游戏流程设…

游戏引擎学习第142天

今天的计划 欢迎来到这个游戏开发项目&#xff0c;我们将从零开始编写一个完整的游戏&#xff0c;并且不会使用任何现成的库或引擎。整个开发过程中涉及的所有代码都会被完整展示&#xff0c;包括游戏运行所需的每一个细节。无论是哪款游戏&#xff0c;最终都需要有人编写底层…

Manus全球首个通用Agent,Manus AI:Agent应用的ChatGPT时刻

文章目录 前言Manus AI: 全球首个通用AgentManus AI: 技术架构与创始人经历AI Agent的实现框架与启示AI Agent的发展预测行业风险提示 前言 这是一篇关于Manus AI及其在通用人工智能领域的应用和前景的报告&#xff0c;主要介绍了Manus AI的产品定位、功能、技术架构、创始人经…

FPGA学习篇——Verilog学习3(关键字+注释方法+程序基本框架)

1 Verilog常用关键字 大概知道以下哪些是关键字就好&#xff0c;如何使用还是得在编写代码中来学习。 2 Verilog注释方法 Verilog有两种注释方式&#xff1a; 2.1 “ // ” 单行。 2.2 “ /* ... */ ” 可扩展多行。 3 Verilog程序基本框架 Verilog 的基本设计单元是“…

一文对比RAGFLOW和Open WebUI【使用场景参考】

一、RAGFLOW与Open WebUI RAGFLOW是一款基于深度文档理解构建的开源 RAG&#xff08;Retrieval-Augmented Generation&#xff09;引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程&#xff0c;结合大语言模型&#xff08;LLM&#xff09;针对用户各类不…

SyntaxError: Missing semicolon

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

游戏引擎学习第140天

回顾并为今天的内容做准备 目前代码的进展到了声音混音的部分。昨天我详细解释了声音的处理方式&#xff0c;声音在技术上是一个非常特别的存在&#xff0c;但在游戏中进行声音混音的需求其实相对简单明了&#xff0c;所以今天的任务应该不会太具挑战性。 今天我们会编写一个…

vue3如何配置环境和打包

很多新手友友们或刚从vue2切换到vue3的同学&#xff0c;对vue3不同环境配置和打包有很多困惑的地方&#xff0c;Jenna这就把vue3打包配置流程详细的写下来&#xff0c;你们只需要copy就好啦 1.创建环境文件 当我们把项目拿到手&#xff0c;只需要创建三个环境文件&#xff1a…

《AJAX:前端异步交互的魔法指南》

什么是AJAX AJAX&#xff08;Asynchronous JavaScript and XML&#xff0c;异步 JavaScript 和 XML&#xff09; 是一种用于创建异步网页应用的技术&#xff0c;允许网页在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并局部更新页面内容。尽管名称中包含 XML&…

STM32-I2C通信协议

目录 一&#xff1a;什么是I2C通信协议 二&#xff1a;I2C通信 三&#xff1a;I2C时序图 四&#xff1a;面试常见问题 一&#xff1a;什么是I2C通信协议 I2C&#xff08;Inter-Integrated Circuit&#xff09;协议是一种串口通信协议&#xff0c;用于在集成电路之间传输数…

阿里推出全新推理模型(因果语言模型),仅1/20参数媲美DeepSeek R1

阿里Qwen 团队正式发布了他们最新的研究成果——QwQ-32B大语言模型&#xff01;这款模型不仅名字萌萌哒(QwQ)&#xff0c;实力更是不容小觑&#xff01;&#x1f60e; QwQ-32B 已在 Hugging Face 和 ModelScope 开源&#xff0c;采用了 Apache 2.0 开源协议。大家可通过 Qwen C…

GitCode 助力 vue3-element-admin:开启中后台管理前端开发新征程

源码仓库&#xff1a; https://gitcode.com/youlai/vue3-element-admin 后端仓库&#xff1a; https://gitcode.com/youlai/youlai-boot 开源助力&#xff0c;开启中后台快速开发之旅 vue3-element-admin 是一款精心打造的免费开源中后台管理前端模板&#xff0c;它紧密贴合…

接入DeepSeek,九牧开启AI卫浴新赛道!

2025年或可被称为AI新纪元元年&#xff0c;“具身智能”“智能机器人”“6G”等新词语出现在《政府工作报告》里&#xff0c;国家对制造业转型和“人工智能”的发展提出殷切期望。 近年来&#xff0c;围绕数智化&#xff0c;制造业开启了一场全球竞赛&#xff0c;在无人机、高…

概率、泛化与过拟合

1. 贝叶斯定理 (Bayes Rule) 贝叶斯公式&#xff0c;又称贝叶斯定理、贝叶斯法则&#xff0c;最初是用来描述两个事件的条件概率间的关系的公式&#xff0c;后来被人们发现具有很深刻的实际意义和应用价值。该公式的实际内涵是&#xff0c;支持某项属性的事件发生得愈多&#…

基于Python实现的智能旅游推荐系统(Django)

基于Python实现的智能旅游推荐系统(Django) 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat 系统功能实现 总体设计 系统实现 系统首页模块 统首页页面主要包括首页&#xff0c;旅游资讯&#xff0c;景点信息…

php代码审计工具-rips

代码审计 代码审计就是检查所写的代码中是否有漏洞&#xff0c;检查程序的源代码是否有权限从而被黑客攻击&#xff0c;同时也检查了书写的代码是否规范。通过自动化的审查和人工审查的方式&#xff0c;逐行检查源代码&#xff0c;发现源代码中安全缺陷所造成的漏洞&#xff0…

深入剖析分布式事务:原理、方案与实战指南

引言&#xff1a;为什么分布式事务成为架构师的必修课&#xff1f; 在微服务架构大行其道的今天&#xff0c;单体应用被拆分成多个独立服务。当一次业务操作需要跨多个服务/数据库完成时&#xff0c;传统数据库事务的ACID特性不再适用。订单创建需要同时操作订单服务和库存服务…

C语言100天练习题【记录本】

C语言经典100题&#xff08;手把手 编程&#xff09; 可以在哔哩哔哩找到&#xff08;url:C语言经典100题&#xff08;手把手 编程&#xff09;_哔哩哔哩_bilibili&#xff09; 已解决的天数&#xff1a;一&#xff0c;二&#xff0c;五&#xff0c;六&#xff0c;八&#xf…