Trie字典树

文章目录



什么是 T r i e Trie Trie

一种树结构,用来存储字符串,能够查询某字符串是否存在

Trie

  • 由一个统一的根节点 r o o t root root 发散开,存储字符
    • 如果下一个字符之前有用过,就顺着之前的路线往后走
    • 如果下一个字符与之前的某串不重合,就另开一个路线继续走下去
  • 最后如果串存完了在末尾打个标记
    • 比如:之前存过字符串 ′ a b c d e f ′ 'abcdef' abcdef,我们再存 ′ a b c ′ 'abc' abc 就会发现后者是前者的子串,如果不打标记就发现不了这个串

一般条件

一般题目中都会说明是全大写字母或者全小写字母,所以说数组开的范围不会太大,当然也有大范围。(我在写啥???《-_-》)


AcWing 835. Trie字符串统计

板子题:https://www.acwing.com/activity/content/problem/content/883/
题目

CODE

#include <iostream>using namespace std;// 定义常量N为100010,这是我们预设的最大节点数量
const int N = 100010;// son数组用于存储每个节点的子节点,每个节点有26个子节点,对应26个英文字母
// cnt数组用于存储每个节点结束的单词数量
// idx用于节点编号,每当我们创建一个新节点时,idx会加1
// str用于存储输入的字符串
int son[N][26], cnt[N], idx;
char str[N];// 插入函数,用于将一个字符串插入到字典树中
void insert(char *str)
{// p是当前节点的编号,一开始我们在根节点,所以p=0int 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];		// 不能标记为idx,因为可能前面部分串有过记录,用idx就不对了}// 遍历完字符串后,我们在一个节点结束,所以该节点的单词数量加1cnt[p] ++ ;				// 这个也是同理,不能用idx
}// 查询函数,用于查询一个字符串在字典树中出现的次数
int query(char *str)
{// p是当前节点的编号,一开始我们在根节点,所以p=0int p = 0;// 遍历输入字符串的每个字符for (int i = 0; str[i]; i ++ ){// 计算当前字符对应的编号int u = str[i] - 'a';// 如果当前节点没有对应的子节点,说明字符串不存在,返回0if (!son[p][u]) return 0;// 转移到子节点p = son[p][u];}// 返回当前节点的单词数量,即字符串在字典树中出现的次数return cnt[p];
}// 主函数
int main()
{// n是操作数量int n;scanf("%d", &n);// 处理每个操作while (n -- ){// op是操作类型,str是操作的字符串char op[2];scanf("%s%s", op, str);// 如果操作类型是"I",则插入字符串if (*op == 'I') insert(str);// 否则,查询字符串并打印出现次数else printf("%d\n", query(str));}return 0;
}

解释一下 i n s e r t ( ) insert() insert() 函数

  • 首先读入了待插入的字符串str
  • 找到根节点 r o o t root root —> p = 0,也就是在son[0]位置是我们的根节点位置,由这个位置往后寻找
    • 先读入下一个字符,然后在后面 26 26 26 个字符空间中看这个字符是否被标记过
      • 如果被标记过则说明之前的某一字符串的这部分跟插入的串重合,拿着标记号去跟着之前的串走,也就是代码:
      p = son[p][u];
      
      • 如果未标记则说明之前没有串跟它重合,则需要自己开一条线往后走,同时给这个字符赋予新的编号++idx,代表我走过这条路,索引号是idx,也就是代码:
      if (!son[p][u]) son[p][u] = ++ idx;
      
  • 最后串读完了,打个标记,就是字符串最后的字符拿到的编号为索引的数量数组cnt[p]++
  • 对于 q u e r y ( ) query() query() 函数也是一样的思路

i d x idx idx 的意义

idx

1
2

素材来源:https://www.acwing.com/solution/content/5673/ の评论区
还是评论区大神多啊,orz %%%


AcWing 143. 最大异或对

题目链接:https://www.acwing.com/activity/content/problem/content/884/
异或对

异或

  • 异或俗称不进位加法,操作就是将两个二进制数的每一位对比,如果两位不一样(一个 0 0 0 一个 1 1 1 ),那么就记为 1 1 1 ,否则记为 0 0 0
  • 这种运算等价于将两个二进制数加起来,但是每位不进位

思路解析

  • 既然求异或最大,先固定一个数,遍历另一个数,那么最完美的情况就是每一位都不一样,此时异或值最大,虽然这个数存在是有可能的,但是存在一个这样的数不太可能。
  • 那么虽然每一位完全不同的数可能不存在,那我们就利用贪心思想,每一位尽可能选择不一样的,最后得到的就是我们需要的数
  • 由于是随机两个数进行组合,我们只需要固定一个数,然后枚举之前插入过 T r i e Trie Trie 树里面的数即可,最后再把这个数插入字典树

思路

CODE

万事可暴力:

int main(){int n;cin >> n;for(int i = 0; i < n; ++i) scanf("%d", &a[i]);int res = 0;for(int i = 0; i < n; ++i){for(int j = 0; j < i; ++j){res = max(res, a[i] ^ a[j]);}}
}

充斥了暴力美学啊,可惜我猪脑不动的,暴力都没想到打。

T r i e Trie Trie

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 1e5 + 10, M = 31 * N; // 定义数组的大小
int n; // 元素的数量
int son[M][2], idx; // Trie数据结构// 将一个数字插入到trie中的函数
void insert(int a){int p = 0;// 遍历每个位for(int i = 30; i >= 0; --i){int u = a >> i & 1;if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}
}// 查询函数
int query(int a){int p = 0, num = 0;for(int i = 30; i >= 0; --i){int u = a >> i & 1;if(son[p][!u]){num = (num << 1) + !u;p = son[p][!u];}else{num = (num << 1) + u;p = son[p][u];}}return num;
}int main()
{cin >> n;int a, res = 0;while (n -- ){scanf("%d", &a);insert(a);int t = query(a);res = max(res, a ^ t);}cout << res << endl;
}

代码解析

  • 读入数a,先将其插入到字典树中,然后寻找树中插入过的数字与其“最大配偶”最接近的一个
    • i n s e r t ( ) insert() insert():将a按位插入字典树中,从高位到低位

      T i p Tip Tip i n t int int 类型只有 31 31 31 位表示数据的值,最高位的 0 o r 1 0\ or\ 1 0 or 1 代表了正负

    • q u e r y ( ) query() query():按位搜寻,如果存在位的值相反的数,那么就随着相反的值走,不存在就随相同的值走
  • 将查询得到的配偶取异或,存最大值

给点思考

  • 不管是 T r i e Trie Trie 还是字符哈希,他们好像都是查询作用的结构
    • T r i e Trie Trie 树:按每个字符进行查询
    • 字符哈希:按照区间进行查询
    • 所以说想要用字符串查询,用 T r i e Trie Trie 树;想要用区间 [ l , r ] [l, r] [l,r] 来查询,得用哈希表
  • 对于 T r i e Trie Trie 来说,理论上可以存储任何数据,因为所有数据终究是一堆 0 0 0 1 1 1 ,那么用 T r i e Trie Trie 存的话就变成了一棵二叉树,所以说 T r i e Trie Trie 不仅可以存字符串,也可以存数字
  • 如果我们再遇到从一堆数里面拿出两个进行操作时,我们可以像本题一样,先枚举第一个,第二个枚举到第一个数为止
    • 比如 ( 1 , 2 , 3 , 4 ) (1, 2, 3, 4) (1,2,3,4),我们i枚举到 3 3 3i, j组合就是 ( 1 , 3 ) , ( 2 , 3 ) , ( 3 , 3 ) (1, 3), (2, 3), (3, 3) (1,3),(2,3),(3,3) ,那 ( 4 , 3 ) (4, 3) (4,3) 咋办?我们枚举 4 4 4 的时候自然会枚举到 ( 4 , 3 ) (4, 3) (4,3)
    • 也就是公式 C n 2 = n × ( n − 1 ) 2 C_{n}^{2} = \frac{n \times (n - 1)}{2} Cn2=2n×(n1)

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

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

相关文章

NX二次开发UF_MTX2_vec_multiply_t 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_MTX2_vec_multiply_t Defined in: uf_mtx.h void UF_MTX2_vec_multiply_t(const double vec [ 2 ] , const double mtx [ 4 ] , double vec_product [ 2 ] ) overview 概述 Ret…

《开箱元宇宙》:Madballs 解锁炫酷新境界,人物化身系列大卖

你是否曾想过&#xff0c;元宇宙是如何融入世界上最具代表性的品牌和名人的战略中的&#xff1f;在本期的《开箱元宇宙》 系列中&#xff0c;我们与 Madballs 的战略顾问 Derek Roberto 一起聊聊 Madballs 如何在 90 分钟内售罄 2,000 个人物化身系列&#xff0c;以及是什么原…

软考:2024年软考高级:软件工程

软考&#xff1a;2024年软考高级: 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &#xff08;1…

进程间的信号

1. 进程信号 1.1 进程信号的概念 信号是一个软件中断&#xff0c;通知进程某个事件发生了异步事件&#xff0c;打断进程当前的操作&#xff0c;去处理这个事件&#xff0c;信号是多种多样的&#xff0c;并且一个信号对应一个事件&#xff0c;这样才能做到进程收到一个信号后&…

解决:ValueError: the first two maketrans arguments must have equal length

解决&#xff1a;ValueError: the first two maketrans arguments must have equal length 文章目录 解决&#xff1a;ValueError: the first two maketrans arguments must have equal length背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在…

Java | The last packet sent successfully to the server was xxx milliseconds ago

最近在部署代码后&#xff0c;后端总是会遇到这个问题&#xff0c;设备通道在访问数据库时经常会报错&#xff0c;在搜集大量资料后我以为是配置问题&#xff0c;首先要保证&#xff1a; &#xff08;1&#xff09;首先确定jdbc.url地址是正确的 &#xff08;2&#xf…

【C++】类和对象——const修饰成员函数和取地址操作符重载

在上篇博客中&#xff0c;我们已经对于日期类有了较为全面的实现&#xff0c;但是&#xff0c;还有一个问题&#xff0c;比如说&#xff0c;我给一个const修饰的日期类的对象 这个对象是不能调用我们上篇博客写的函数的&#xff0c;因为&d1是const Date*类型的&#xff…

操作系统--中断异常

操作系统第一章易错总结 1.操作系统的功能 ⭐ 编译器是操作系统的上层软件&#xff0c;不是操作系统需要提供的功能。 ⭐注意&#xff1a; 1.批处理的主要缺点是缺乏交互性 2.输入/输出指令需要中断操作&#xff0c;中断必须在核心态下执行 3.多道性是为了提高系统利用率和…

【模电】放大电路的性能指标

放大电路的性能指标 放大倍数输入电阻输出电阻通频带非线性失真系数最大不失真输出电压最大输出功率与效率 下图所示为放大电路的示意图。 对于信号而言&#xff0c;任何一个放大电路均可看成一个两端口网络。左边为输入端口&#xff0c;当内阻为 R s R\tiny s Rs的正弦波信号…

Java多线程-第20章

Java多线程-第20章 1.创建线程 Java是一种支持多线程编程的编程语言。多线程是指在同一程序中同时执行多个独立任务的能力。在Java中&#xff0c;线程是一种轻量级的子进程&#xff0c;它是程序中的最小执行单元。Java的多线程编程可以通过两种方式实现&#xff1a;继承Threa…

项目五 配置与管理磁盘

项目五 配置与管理磁盘 磁盘配额&#xff08;Quota&#xff09;&#xff0c;磁盘阵列&#xff08;RAID&#xff09;&#xff0c;逻辑滚动文件系统&#xff08;LVM&#xff09; #职业能力目标和要求 1&#xff0c;掌握Linux下的磁盘管理工具的使用方法 2&#xff0c;掌握Linux…

分享Python7个爬虫小案例(附源码)

本次的7个python爬虫小案例涉及到了re正则、xpath、beautiful soup、selenium等知识点&#xff0c;非常适合刚入门python爬虫的小伙伴参考学习。注&#xff1a;若涉及到版权或隐私问题&#xff0c;请及时联系我删除即可。 1.使用正则表达式和文件操作爬取并保存“某吧”某帖子…