【算法基础:数据结构】2.2 字典树/前缀树 Trie

文章目录

  • 知识点
    • cpp结构体模板
  • 模板例题
    • 835. Trie字符串统计❤️❤️❤️❤️❤️(重要!模板!)
    • 143. 最大异或对😭😭😭😭😭(Trie树的应用)
  • 相关题目练习
    • 208. 实现 Trie (前缀树)
    • 1804. 实现 Trie (前缀树) II
  • 参考资料

知识点

用于高效地存储和查找字符串集合的数据结构——Trie树

https://oi-wiki.org/string/trie/
在这里插入图片描述
可以发现,这棵字典树用边来代表字母,而从根结点到树上某一结点的路径就代表了一个字符串。举个例子, 1 → 4 → 8 → 12 1\to4\to 8\to 12 14812 表示的就是字符串 caa

这类题目,字母的种类不会很多。

cpp结构体模板

struct trie {
  int nex[100000][26], cnt;
  bool exist[100000];  // 该结点结尾的字符串是否存在

  void insert(char *s, int l) {  // 插入字符串
    int p = 0;
    for (int i = 0; i < l; i++) {
      int c = s[i] - 'a';
      if (!nex[p][c]) nex[p][c] = ++cnt;  // 如果没有,就添加结点
      p = nex[p][c];
    }
    exist[p] = 1;
  }

  bool find(char *s, int l) {  // 查找字符串
    int p = 0;
    for (int i = 0; i < l; i++) {
      int c = s[i] - 'a';
      if (!nex[p][c]) return 0;
      p = nex[p][c];
    }
    return exist[p];
  }
};

模板例题

835. Trie字符串统计❤️❤️❤️❤️❤️(重要!模板!)

https://www.acwing.com/activity/content/problem/content/883/

在这里插入图片描述

代码模板在于 insertquery 这两个方法的写法。

除此之外要理解数组 soncnt 和变量 idx 的含义。(含义已经写在代码注释里了

son[][] 的第一维是可能出现的字符**数量**的最大值;第二维是可能出现的字符**种类**的最大值。
cnt[] 的大小是可能出现的字符数量的最大值,也就是记录每个节点作为了几次字符串的末尾。
idx 记录出现了几个新的节点。
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.*;

public class Main {
    static final int N = 100010;    // 所有输入的字符串总长度不超过 10^5
    static int[][] son;
    static int[] cnt;
    static int idx;             // idx递增作为节点的序号
    static {
        son = new int[N][26];   // 记录各个节点的儿子
        cnt = new int[N];       // 记录各个节点作为结尾的次数
    }


    public static void main(String[] args) throws IOException {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int n = sin.nextInt();
        while (n-- > 0) {
            char op = sin.next().charAt(0);
            String s = sin.next();
            if (op == 'I') {
                insert(s.toCharArray());
            } else {
                System.out.println(query(s.toCharArray()));
            }
        }

        bw.flush();
    }

    // 插入一个字符串
    static void insert(char[] str) {
        int p = 0;
        for (int i = 0; i < str.length; ++i) {		// 枚举每个字符
            int u = str[i] - 'a';
            if (son[p][u] == 0) son[p][u] = ++idx;  // 如果当前层不存在u的话,新建一个节点
            p = son[p][u];
        }
        cnt[p]++;           // 作为结尾的情况+1
    }

    static int query(char[] str) {
        int p = 0;
        for (int i = 0; i < str.length; ++i) {
            int u = str[i] - 'a';
            if (son[p][u] == 0) return 0;
            p = son[p][u];
        }
        return cnt[p];
    }
}

143. 最大异或对😭😭😭😭😭(Trie树的应用)

https://www.acwing.com/problem/content/145/

在这里插入图片描述

异或运算:相同得 0 ,不同得 1。(俗称不进位加法)

从高位开始比较。

检查到有反的,就可以 += 1 << i;

import java.util.Scanner;

public class Main {
    final static int M = 31 * 100010;   // M 是 Trie树中最多可能的节点数量
    static int[][] son = new int[M][2];
    static int idx = 0;

    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), ans = 0;
        for (int i = 0; i < n; ++i) {
            int a = scanner.nextInt();
            ans = Math.max(find(a), ans);
            insert(a);
        }
        System.out.println(ans);
    }
    
    // 从高位到低位插入
    public static void insert(int x) {
        int p = 0;
        for (int i = 30; i >= 0; --i) {
            int u = x >> i & 1;
            if (son[p][u] == 0) son[p][u] = ++idx;
            p = son[p][u];
        }
    }

    public static int find(int x) {
        int p = 0, res = 0;
        for (int i = 30; i >= 0; --i) {
            int u = x >> i & 1;         // 获得当前位
            if (son[p][u ^ 1] != 0) {   // 检查当前位有没有取反的
                res += 1 << i;
                p = son[p][u ^ 1];
            } else p = son[p][u];
        }
        return res;
    }
}

相关题目练习

208. 实现 Trie (前缀树)

https://leetcode.cn/problems/implement-trie-prefix-tree/
在这里插入图片描述
提示:

1 <= word.length, prefix.length <= 2000
word 和 prefix 仅由小写英文字母组成
insert、search 和 startsWith 调用次数 总计 不超过 3 * 10^4 次

一道练习Trie树模板的题目。

class Trie {
    final int N = 200001 + 1;
    int[][] son = new int[N][26];
    int[] cnt = new int[N];
    int idx = 0;

    public Trie() {

    }
    
    public void insert(String word) {
        int p = 0;
        for (char ch: word.toCharArray()) {
            int u = ch - 'a';
            if (son[p][u] == 0) son[p][u] = ++idx;
            p = son[p][u];
        }
        ++cnt[p];
    }
    
    public boolean search(String word) {
        int p = 0;
        for (char ch: word.toCharArray()) {
            int u = ch - 'a';
            if (son[p][u] == 0) return false;
            p = son[p][u];
        }
        return cnt[p] > 0;
    }
    
    public boolean startsWith(String prefix) {
        int p = 0;
        for (char ch: prefix.toCharArray()) {
            int u = ch - 'a';
            if (son[p][u] == 0) return false;
            p = son[p][u];
        }
        return true;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */ 

1804. 实现 Trie (前缀树) II

1804. 实现 Trie (前缀树) II

在这里插入图片描述
提示:

1 <= word.length, prefix.length <= 2000
word 和 prefix 只包含小写英文字母。
insert、 countWordsEqualTo、 countWordsStartingWith 和 erase 总共调用最多 3 * 10^4 次。
保证每次调用 erase 时,字符串 word 总是存在于前缀树中。

相比上一题,多开一个数组 cnt2 记录一下各个节点被经过了多少次就好了

class Trie {
    int[][] son = new int[30000][26];
    int[] cnt = new int[30000], cnt2 = new int[30000];
    int idx = 0;

    public Trie() {

    }
    
    public void insert(String word) {
        int p = 0;
        for (char ch: word.toCharArray()) {
            int u = ch - 'a';
            if (son[p][u] == 0) son[p][u] = ++idx;
            p = son[p][u];
            cnt2[p]++;
        }
        cnt[p]++;
    }
    
    public int countWordsEqualTo(String word) {
        int p = getP(word);
        return cnt[p];
    }
    
    public int countWordsStartingWith(String prefix) {
        int p = getP(prefix);
        return cnt2[p];
    }
    
    public void erase(String word) {
        int p = 0;
        for (char ch: word.toCharArray()) {
            int u = ch - 'a';
            p = son[p][u];
            cnt2[p]--;
        }
        cnt[p]--;
    }

    public int getP(String s) {
        int p = 0;
        for (char ch: s.toCharArray()) {
            int u = ch - 'a';
            if (son[p][u] != 0) p = son[p][u];
            else return 0;
        }
        return p;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * int param_2 = obj.countWordsEqualTo(word);
 * int param_3 = obj.countWordsStartingWith(prefix);
 * obj.erase(word);
 */

参考资料

https://oi-wiki.org/string/trie/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【UE4 塔防游戏系列】06-炮塔发射子弹攻击敌人

效果 步骤 1. 新建一个Actor蓝图类&#xff0c;命名为“TotalBulletsCategory”&#xff0c;用来表示子弹蓝图总类&#xff0c;后面会有很多不同类型的子弹会继承该类 打开“TotalBulletsCategory”&#xff0c;添加粒子系统组件、盒体碰撞组件和发射物移动组件 调整发射物重力…

电压放大器在超声波焊接中的作用以及应用

电压放大器是一种运用于电子设备中的信号放大器&#xff0c;主要作用是将小信号放大为更高幅度的信号。在超声波焊接中&#xff0c;电压放大器起到了重要的作用&#xff0c;它可以将从传感器采集到的微小信号放大为能够被检测和处理的合适大小的信号。 超声波焊接是现代工业生产…

使用shell监控应用运行状态通过企业微信接收监控通知

目的&#xff1a;编写shell脚本来监控应用服务运行状态&#xff0c;若是应用异常则自动重启应用通过企业微信接收监控告警通知 知识要点&#xff1a; 使用shell脚本监控应用服务使用shell脚本自动恢复异常服务通过企业微信通知接收监控结果shell脚本使用数组知识&#xff0c;…

二次元少女-InsCode Stable Diffusion 美图活动一期

一、 Stable Diffusion 模型在线使用地址&#xff1a; https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型相关版本和参数配置&#xff1a; 模型版本&#xff1a;chilloutmix_NiPrunedFp32Fix.safetensors 采样方法(Sampler)Sampling method&#xff1a;DPM SDE …

xpath下载安装——Python爬虫xpath插件下载安装(2023.7亲测可用!!)

目录 1.免费下载插件链接&#xff08;若失效评论区留言发送最新链接&#xff09;&#xff08;2023.7亲测可用&#xff09; 2.安装插件 &#xff08;1&#xff09;打开chrome浏览器页面&#xff0c;点击&#xff1a;右上角三个点 > 扩展程序 > 管理拓展程序 &#xff…

2023-7-19-第二十式迭代器模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

TortoiseGit 入门指南12:创建标签

前面的文章不止一次的提到过 标签 &#xff08;Tag&#xff09;&#xff0c;我们在《TortoiseGit 入门指南08&#xff1a;浏览引用以及在引用间切换》一文中知道&#xff0c;标签 是一种 引用&#xff1b;还知道每个提交都对应着一个 SHA-1 值&#xff0c;而引用就是 SHA-1 的一…

SuperGlue学习记录之最优传输

在进行最优传输相关理论的学习过程中&#xff0c;找到SuperGlue这篇论文&#xff0c;该篇论文通过最优传输来完成特征点的匹配过程。 SuperGlue结构 先来看一下其结构&#xff1a; 首先将两张图片送入特征提取网络&#xff0c;通过卷积网络提取出特征&#xff0c;主要有四个值…

Generative Adversarial Network

Goodfellow,2014年 文献阅读笔记--GAN--Generative Adversarial NetworkGAN的原始论文-组会讲解_gan英文论文_Flying Warrior的博客-CSDN博客 启发:如何看两个数据是否来自同一个分布? 在统计中,two sample test。训练一个二分类的分类器,如果能分开这两个数据,说明来自…

网络安全—信息安全—黑客技术(学习笔记)

一、什么是网络安全&#xff1f; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都…

[深度学习入门]什么是神经网络?[神经网络的架构、工作、激活函数]

目录 一、前言二、神经网络的架构——以手写数字识别三、神经网络的工作1、单输入单输出感知器函数2、二维输入参数3、三维输入参数 四、激活函数1、激活函数2、ReLU激活函数3、非线性激活函数&#xff08;1&#xff09;二输入二输出的神经网络的架构&#xff08;2&#xff09;…

计算机网络 day8 动态路由 - NAT - SNAT实验 - VMware的网卡的3种模式

目录 动态路由&#xff1a;IGP 和 EGP 参考网课&#xff1a;4.6.1 路由选择协议概述_哔哩哔哩_bilibili ​编辑 IGP&#xff08;Interior Gateway Protocol&#xff09;内部网关协议&#xff1a; EGP&#xff08;Interior Gateway Protocol&#xff09;外部网关协议&#x…

使用模板创建【vite+vue3+ts】项目出现 “找不到模块‘vue‘或其相应的类型声明” 的解决方案

问题描述 项目前台需要使用Vue3Ts来写一个H5应用&#xff0c;然后我用模板创建 npm create vitelatest vue3-vant-mobile -- --template vue-ts创建完后进入HelloWorld.vue&#xff0c;两眼一黑 解决办法一 npm i --save-dev types/node然后在tsconfig.json的"compi…

软件测试银行项目面试过程

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

Kind | Kubernetes in Docker 把k8s装进docker!

有点像杰克船长的黑珍珠 目录 零、说明 一、安装 安装 Docker 安装 kubectl 安装 kind 二、创建/切换/删除集群 创建 切换 删除 将镜像加载到 kind 群集中 零、说明 官网&#xff1a;kind Kind&#xff1a; Kubernetes in Docker 的简称。kind 是一个使用 Docker 容…

TortoiseGit 入门指南11:还原与重置

Git 就像个时光机器&#xff0c;能让我们还原到任何提交。 还原未提交的更改 假如我们在查看一个干净的代码仓库&#xff0c;干净意味着工作区中的文件保持着最后一次提交的状态&#xff0c;没有修改。在查看的过程中&#xff0c;我们有意或无意的修改了工作区中的文件&#…

MySQL数据库(五)

目录 一、数据库的约束 1.1 约束类型 1.1.1 null约束 1.1.2unique约束 1.1.3default默认值约束 1.1.4primary key主键约束 1.1.5foreign key外键约束 二、内容重点总结 一、数据库的约束 1.1 约束类型 not null - 指示某列不能存储 null值。unique - 保证某列的每行必须有唯一…

blender 叶片制作

圆润叶片 效果展示 shift a 新建矩形&#xff0c;s y 延 y 轴方向压扁&#xff0c;ctrl r 循环切割&#xff0c;滚动滚轮&#xff0c;延 y 轴方向切两条循环线&#xff0c;框选点&#xff0c;s 缩放&#xff0c;调整到叶片造型&#xff0c;添加细分修改器&#xff1b;给叶片…

opencv-14 图像加密和解密

在OpenCV中&#xff0c;图像加密和解密是通过对图像像素进行一系列的变换和操作来实现的 通过按位异或运算可以实现图像的加密和解密。 通过对原始图像与密钥图像进行按位异或&#xff0c;可以实现加密&#xff1b;将加密后的图像与密钥图像再次进行按位异或&#xff0c;可以实…

CodeGeex论文阅读

《CodeGeeX: A Pre-Trained Model for Code Generation with Multilingual Evaluations on HumanEval-X》 论文地址&#xff1a;https://arxiv.org/pdf/2303.17568.pdf 代码地址&#xff1a;https://github.com/THUDM/CodeGe 一、简介 CodeGeeX&#xff0c;是一个具有130亿…