算法------(12)Trie树(字典树)

例题:(1)Acwing 835. Trie字符串统计

        Trie树是一个可以高效存储查询字符串的数据结构。将一个字符串的每一个字符作为一个根节点,从字符串头到字符串尾连接起来。因此我们可以把每一个字符串存储为一个节点,记录其子节点的位置。存储时对每一个字符串尾进行标记,查询时从根节点开始遍历。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
const int N = 1e5+10,M = 26;
int son[N][M],idx,cnt[N];
void insert(string str){int len = str.length(),p = 0;for(int i = 0;i<len;i++){int t = str[i] - 'a';if(!son[p][t]) son[p][t] = ++idx;p = son[p][t];}cnt[p]++; 
}
int query(string str){int len = str.length(),p = 0;for(int i = 0;i<len;i++){int t = str[i] - 'a';if(!son[p][t]) return 0;p = son[p][t];}return cnt[p];
}
int main()
{int n;scanf("%d", &n);while(n--){char op[2];scanf("%s", op);string str;cin >> str;if(op[0] == 'I'){insert(str);}else{printf("%d\n",query(str));}}return 0;
}

(2) AcWing 143. 最大异或对

        暴力做法肯定会超时,因此我们对xor运算进行考虑。

        每个数可以被看做31位的二进制数,而xor运算的最大值意味着从最高位开始尽量不同。因此可以用trie树进行存储,存储每一个数并对其与前面已存在的trie树进行xor运算(查看每一位所在的节点是否有与其不同的儿子存在,如果有则*2+1,否则*2)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 31e5+10;
int idx,son[N][2];
void insert(int n){int p = 0;for(int i = 30;i>=0;i--){int t = n >> i & 1;if(!son[p][t]) son[p][t] = ++idx;p = son[p][t];}
}
int query(int x){int res = 0,p = 0;for(int i = 30;i>=0;i--){int t = x >> i & 1;if(son[p][!t]){p = son[p][!t];res = res*2 + 1;}else{p = son[p][t];res *= 2;}}return res;
}
int main()
{int n;scanf("%d",&n);int res = 0;for(int i = 0;i<n;i++){int x;scanf("%d", &x);insert(x);res = max(res,query(x));}printf("%d",res);return 0;
}

练习:(1)Leetcode 211 添加与搜索单词

         。。生病因此状态不佳。。

        这个Trie树的查找相比一般的Trie树更为麻烦,因为他的搜索字符串中含有万能字符‘.’,所以在匹配时需要分情况讨论,一般字符正常匹配即可,而字符为‘.’时遍历判断其对应的节点是否有子节点即可。因此需要用爆搜来实现。

class WordDictionary {
public:int idx = 0,son[250010][26] = {0};bool vis[250010] = {0};WordDictionary() {}void addWord(string word) {int len = word.length(),p = 0;for(int i = 0;i<len;i++){int u = word[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}vis[p] = true;}bool search(string word) {return dfs(word,0,0);}bool dfs(string word,int index,int root){if(index == word.length()) return vis[root];bool f = false;if(word[index] == '.'){for(int i = 0;i<26;i++){if(son[root][i] == 0) continue;f = dfs(word,index+1,son[root][i]);if(f) break;}}else{if(son[root][word[index] - 'a']){f = dfs(word,index + 1,son[root][word[index]-'a']);}}return f;}
};

(2)Acwing 161.电话列表

         题目没什么难的。。贴上来是为了让大家注意坑点。。输入不能随意跳出循环否则剩下没输入的会顺延输入导致全部错乱。。。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 600010,M = 10;
int son[N][M],idx;
bool cnt[N];
bool query(string x){int len = x.length(),p = 0;for(int i = 0;i<len;i++){int u = x[i] - '0';if(!son[p][u]) return false;p = son[p][u];if(cnt[p]) return true;}return true;
}
void insert(string x){int len = x.length(),p = 0;for(int i = 0;i<len;i++){int u = x[i] - '0';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}cnt[p] = true;
}
void init(){idx = 0;memset(son[0],0,sizeof(son));memset(cnt,0,sizeof(cnt));
}
int main()
{int t;scanf("%d", &t);while(t--){init();int n;bool flag = 0;scanf("%d", &n);for(int i = 0;i<n;i++){string str;cin >> str;if(flag) continue;if(!query(str)) insert(str);else{printf("NO\n");flag = 1;}}if(!flag) printf("YES\n");}return 0;
}

(3)Acwing 142. 前缀统计


         对父子节点的关系还是不太清楚。。这题其实没啥难度,主要是何时求取节点标记需要好好思考。一定是先更新到子节点,再对标记值累加。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6+10,M = 26;
int son[N][M],idx,cnt[N];
void insert(string str){int p = 0,len = str.length();for(int i = 0;i<len;i++){int u = str[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}cnt[p]++;
}
int query(string str){int p = 0,len = str.length(),res = 0;for(int i = 0;i<len;i++){int u = str[i] - 'a';if(!son[p][u]) break;p = son[p][u];res += cnt[p];}return res;
}
int main()
{int n,m;scanf("%d%d", &n, &m);for(int i = 0;i<n;i++){string str;cin >> str;insert(str);}for(int i = 0;i<m;i++){string str;cin >> str;printf("%d\n",query(str));}return 0;
}

(4)Acwing 5304. 最高频字符串

        讲道理其实还是没啥难度的题。。还是想记录个小细节。。

        当一个函数同时存在返回值和其他附加功能的时候(比如并查集的find函数存在路径加速和返回根节点两个作用),一定要注意不要重复调用,否则可能会出现重复累加(带权并查集也是如此。。)这题的insert函数就是很好的例子。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,M = 26;
int son[N][M],idx,cnt[N];
int insert(string str){int p = 0,len = str.length();for(int i = 0;i<len;i++){int u = str[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}cnt[p]++;return cnt[p];
}
int main()
{int n;scanf("%d", &n);int res = -1;string ans = "";for(int i = 0;i<n;i++){string c;cin >> c;int k = insert(c);if(k > res || (k == res && c < ans) ){ans = c;res = k;}}cout << ans;return 0;
}

(5) Leetcode 1065.字符串的索引对

         我还以为有什么好方法。。原来也是无脑暴力。。

        把word里的字符串存入Trie树,然后枚举text中的每一个子串去Trie树中查询。

class Solution {int son[1010][26],idx;bool cnt[1010] = {0};
public:void insert(string x){int p = 0,len = x.length();for(int i=0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}cnt[p] = true;}bool query(string x){int p = 0,len = x.length();for(int i=0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) return false;p = son[p][u];}return cnt[p];}vector<vector<int>> indexPairs(string text, vector<string>& words) {vector<vector<int>> res;int sz = words.size();for(int i = 0;i<sz;i++){insert(words[i]);}int len = text.length();for(int i = 0;i<len;i++){for(int j = 1;j<=len-i;j++){string str = text.substr(i,j);cout << str << endl;if(query(str)) res.push_back({i,i+j-1});}}return res;}
};

(6) Leetcode 14.最长公共前缀

        讲道理其实没啥难度。。想了好一会来着。。用字典树去做其实复杂了。。。不过是字典树专题。。 

        对每一个字符串先查询再存储,因为我们要找的是整个数组中字符串的公共前缀,因此我们每次查询时都需要把结果更新成最短的那个公共前缀。同时要注意特殊情况(一个字符串),第一个字符串最好先插入。

class Solution {int son[40010][26],idx;
public:void insert(string x){int p = 0,len = x.length();for(int i = 0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}}int query(string x){int p = 0,len = x.length(),res = 0;for(int i = 0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) return res;p = son[p][u];res++;}return res;}string longestCommonPrefix(vector<string>& strs) {int sz = strs.size();int res = 100000;string ans = strs[0];insert(strs[0]);for(int i = 1;i<sz;i++){if(query(strs[i]) < res){res = query(strs[i]);ans = strs[i].substr(0,res);if(ans == "") return ans;}insert(strs[i]);}return ans;}
};

(7) Leetcode 139.单词拆分

        其实跟Trie没啥关系(因为要Trie+dfs不会。。。)用dp做的,贴上来是为了羞辱一下自己的菜。

        dp[i]指前i个字母是否能被拼凑出来,枚举前i个字母的每一个插空位j,对插空位j前面的字符串,其能否被拼凑可看dp[j],对j后面的字符,利用check函数(截取字符串)判断是否能在哈希set中找到。对于初始状态,规定dp[0] = true。

class Solution {bool dp[310];unordered_set<string> res;
public:bool check(string x){if(res.find(x) != res.end()) return true;return false;}bool wordBreak(string s, vector<string>& wordDict) {int sz = wordDict.size();for(int i = 0;i<sz;i++) res.insert(wordDict[i]);int n = s.length();dp[0] = true;for(int i = 1;i<=n;i++){for(int j = 0;j<i;j++){if(dp[j] && check(s.substr(j,i-j))){dp[i] = true;break;}}}return dp[n];}
};

 (8) Leetcode 720.词典中最长的单词

        无非就是打打标记嘛。。。and注意审题。。。

        要求其他单词“逐步”添加一个字母构成的最长单词,那么对query查找进行改装即可,只遍历数组的前n-1个字母,每一个字母处如果都有标记则符合要求。

class Solution {int son[30010][26],idx;bool cnt[30010];
public:void insert(string x){int p = 0,len = x.length();for(int i = 0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}cnt[p] = true;}bool query(string x){int p = 0,len = x.length();if(len == 1) return true;for(int i = 0;i<len-1;i++){int u = x[i] - 'a';if(!son[p][u]) return false;p = son[p][u];if(!cnt[p]) return false;}return cnt[p];}string longestWord(vector<string>& words) {int n = words.size();for(int i = 0;i<n;i++){insert(words[i]);}string ans = "";int res = 0;for(int i = 0;i<n;i++){int len = words[i].length();string str = words[i];if(query(str) && (res < len|| (res == len && str < ans))){ans = str;res = len;}}return ans;}
};

(9) Leetcode 1268.搜索推荐系统

         一开始就没想出来。。且坑点巨多。。改也改了好久。。

        这道题对字典树的“节点”这个概念有很深的强化。对每一个节点,我们对应一个字符串的大根堆,每次访问这个节点时把当前字符串加入,并且如果该大根堆中字符串数目大于3时,将堆顶的字符串(字典序最大的那个)弹出。对于目标字符串,我们一个节点一个节点进行访问,如果遇到了无相应子节点的节点,则后面全部为空集,这里我们需要一个bool变量来记录是否已经出现该情况,如果只用continue判断会出错(p=0时也意味着p回到根节点)。由于每个节点对应着大根堆,因此每个节点得到的集合还需要取反。

class Solution {int son[200010][26],idx = 0;unordered_map<int,priority_queue<string>> y;
public:void insert(string x){int p = 0,len = x.length();for(int i = 0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u]; y[p].push(x);if(y[p].size() > 3) y[p].pop();}}vector<vector<string>> query(string x){vector<vector<string>> res;int p = 0,len = x.length();bool flag = false;for(int i = 0;i<len;i++){int u = x[i] - 'a';if(flag || !son[p][u]){res.push_back({});flag = true;}else{p = son[p][u]; vector<string> t;while(y[p].size()){t.push_back(y[p].top());y[p].pop();}reverse(t.begin(),t.end());res.push_back(t);}}return res;}vector<vector<string>> suggestedProducts(vector<string>& products, string searchWord) {int n = products.size();for(int i = 0;i<n;i++){insert(products[i]);}return query(searchWord);}
};

(10) Leetcode 2707.字符串中的额外字符

        。。DP做不出啊啊啊啊啊。。。

        记n为字符串长度,我们对n([0,n-1])有两种划分方式,一种是把 s[n-1]当做额外字符,因此dp[n] = dp[n-1] + 1,另一种是查看[j,n-1]是否在字典中可查找到,如果可以则dp[n]又等于dp[j](相当于[j,n-1]已经处理好,只需要再处理[0,j-1])因此对每一个i枚举对应的j即可。

class Solution {int d[60],son[2510][26],idx;bool cnt[2510];
public:void insert(string x){int p = 0,len = x.length();for(int i = 0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) son[p][u] = ++idx;p = son[p][u];}cnt[p] = true;}bool query(string x){int p = 0,len = x.length();for(int i = 0;i<len;i++){int u = x[i] - 'a';if(!son[p][u]) return false;p = son[p][u];}return cnt[p];}int minExtraChar(string s, vector<string>& dictionary) {int n = dictionary.size(),len = s.length();for(int i = 0;i<n;i++){insert(dictionary[i]);}d[0] = 0;n = s.length();for(int i = 1;i<=s.length();i++){d[i] = d[i-1] + 1;for(int j = 0;j<i;j++){if(query(s.substr(j,i-j))) d[i] = min(d[i],d[j]);}}return d[n];}
};

         

 

 

 

 

 

 

 

 

         

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

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

相关文章

C 嵌入式系统设计模式 09:硬件适配器模式

本书的原著为&#xff1a;《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》&#xff0c;讲解的是嵌入式系统设计模式&#xff0c;是一本不可多得的好书。 本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之二&…

性能测试的几个指标范围(CPU,内存,IO,网络)

性能测试中&#xff0c;对服务端的指标监控也是很重要的一个环节。通过对各项服务器性能指标的监控分析&#xff0c;可以定位到性能瓶颈。 后端性能指标有 CPU&#xff0c;内存&#xff0c;网络&#xff0c;jvm&#xff0c;I/O 等等 分析思路 整体系统 CPU 利用率 内存利用…

【论文精读】Diffusion Transformer(DiT)

摘要 以往的Diffusion模型主要以卷积UNet作为主干网络&#xff0c;本文发现U-Net的归纳偏差对Diffusion模型的性能并不是至关重要的&#xff0c;可以用Transformer等取代。通过利用其他视觉方向成熟的Transformer方案和训练方法&#xff0c;Diffusion模型可以从这些架构中获益&…

一篇文章搞懂CDN加速原理

目录 一、什么是CDN CDN对网络的优化作用主要体现在以下几个方面&#xff1a; 二、CDN工作原理 CDN网络的组成元素&#xff1a; 三、名词解释 3.1 CNAME记录&#xff08;CNAME record&#xff09; 3.2 CNAME域名 3.3 DNS 3.4 回源host 3.5 协议回源 一、什么是CDN CD…

【SpringBoot3】Spring Security 常用配置总结

注&#xff1a;本文基于Spring Boot 3.2.1 以及 Spring Security 6.2.1 相关文章 【SpringBoot3】Spring Security 核心概念 【SpringBoot3】Spring Security 常用注解 【SpringBoot3】Spring Security 详细使用实例&#xff08;简单使用、JWT模式&#xff09; 【SpringBoot3】…

【MATLAB GUI】 4. 坐标区和表

看B站up主freexyn的freexyn编程实例视频教程系列36Matlab GUI的学习笔记 文章目录 坐标区表 坐标区 任务要求设计一个图像显示界面&#xff0c;根据选定的周期做出相应的sin函数图像 使用坐标区、弹出式菜单、普通按钮设计页面&#xff0c;弹出式菜单string设置为1、2、3、4代…

【Linux系统化学习】深入理解匿名管道(pipe)和命名管道(fifo)

目录 进程间通信 进程间通信目的 进程间通信的方式 管道 System V IPC&#xff08;本地通信&#xff09; POSIX IPC&#xff08;网络通信&#xff09; 管道 什么是管道 匿名管道 匿名管道的创建 匿名管道的使用 匿名管道的四种情况 匿名管道的五种特性 命名管道 …

iOS总体框架介绍和详尽说明

iOS总体框架介绍和详尽说明 一、iOS是什么&#xff1f;1.iOS简述2.iOS界面 二、iOS总体框架介绍和详尽说明1.iOS的框架层级1. 核心操作系统层&#xff08;Core OS layer&#xff09;1.1 Accelerate Framework1.2 Core Bluetooth Framework1.3 External Accessory Framework1.4 …

【wails】(4):使用wails做桌面应用开发,整合chatgpt-web项目做前端,进行本地开发,web端也可以连调,使用websocket实现

1&#xff0c;视频地址 【wails】&#xff08;4&#xff09;&#xff1a;使用wails做桌面应用开发&#xff0c;整合chatgpt-web项目做前端&#xff0c;进行本地开发&#xff0c;web端也可以连调&#xff0c;使用websocket实现 2&#xff0c;演示效果 启动先是报500 错误&#…

Charles小程序抓包(安卓版)

微信小程序/公众号抓包的主流方式 目前有以下几种&#xff1a; 1、WindowsBurpProxifier&#xff08;配置困难&#xff0c;数据包卡顿) 2、Burp安卓模拟器&#xff08;模拟器卡顿&#xff09; 3、BurpCharles&#xff08;本文测试&#xff0c;抓包完整&#xff0c;放包流畅…

防御保护第八、九、十、十一天笔记

一、内容安全 1、DFI和DPI技术 --- 深度检测技术 DPI是一种基于应用层的流量检测和控制技术&#xff0c;它会对流量进行拆包&#xff0c;分析包头和应用层的内容&#xff0c;从而识别应用程序和应用程序的内容。这种技术增加了对应用层的分析&#xff0c;识别各种应用&#xf…

【2024.02.22】定时执行专家 V7.0 发布 - TimingExecutor V7.0 Release - 龙年春节重大更新版本

目录 ▉ 新版本 V7.0 下载地址 ▉ V7.0 新功能 ▼2024-02-21 V7.0 - 更新日志▼ ▉ V7.0 新UI设计 ▉ 新版本 V7.0 下载地址 BoomWorks软件的最新版本-CSDN博客文章浏览阅读10w次&#xff0c;点赞9次&#xff0c;收藏41次。▉定时执行专家—毫秒精度、专业级的定时任务执行…

Jenkins2.426邮件通知配置

之前安装的jenkins出现问题了&#xff0c;重新装了jenkins&#xff0c;需要重新配置&#xff1a;Maven&#xff0c;JDK&#xff0c;Allure报告&#xff0c;邮件通知&#xff0c;Extended E-mail Notification等 配置Maven&#xff0c;JDK参考&#xff1a;CICD集合(四):Jenkins…

vue中使用wangEditor富文本编辑器

jsd-2306-vue-01: 教学项目教学项目教学项目教学项目教学项目 2306-vue-baking-teacher: 教学项目教学项目教学项目教学项目 一、脚手架工程中使用富文本编辑器wangEditor 1.通过以下命令 安装wangEditor npm i wangeditor -S 2.在main.js文件中添加以下配置信息 //引入wa…

多数pythoneer只知有列表list却不知道python也有array数组

数组和列表 Python中数组和列表是不同的&#xff0c;我敢断言大多数的pythoneer只知道有列表list&#xff0c;却不知道python也有array数组。列表是一个包含不同数据类型的元素集合&#xff0c;而数组是一个只能含相同数据类型的元素集合。 Python的array库是一个提供数组操作…

第七篇【传奇开心果系列】python的文本和语音相互转换库技术点案例示例:Sphinx自动电话系统(IVR)经典案例

传奇开心果博文系列 系列博文目录python的文本和语音相互转换库技术点案例示例系列 博文目录前言一、雏形示例代码二、扩展思路介绍三、Sphinx多语言支持示例代码四、Sphinx和语音合成库集成示例代码五、Sphinx语音识别前自然语言预处理示例代码六、Sphinx语音识别自动电话系统…

echarts 实现x轴文字过长时折行展示

代码如下&#xff1a; this.options {color: ["#0075FF", "#00E2C4", "#FCA884", "#FFCB11"],grid: {top: "25%",bottom: "6%",right: "8%",left: "8%",containLabel: true,},legend: {top…

vscode怎么运行C++的cpp文件

非常简单&#xff0c;点击vscode导航栏的Extensions, 搜索C/C Compile Run安装上即可&#xff0c; 写好 C/C 程序之后&#xff0c;直接使用 F6 一键编译运行就能出结果。

robots.txt 文件规则

robots.txt 是一种用于网站根目录的文本文件&#xff0c;其主要目的在于指示网络爬虫&#xff08;web crawlers&#xff09;和其他网页机器人&#xff08;bots&#xff09;哪些页面可以抓取&#xff0c;以及哪些页面不应该被抓取。可以看作是网站和搜索引擎机器人之间的一个协议…

实验室预约|实验室预约小程序|基于微信小程序的实验室预约管理系统设计与实现(源码+数据库+文档)

实验室预约小程序目录 目录 基于微信小程序的实验室预约管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、微信小程序前台 2、管理员后台 &#xff08;1&#xff09;管理员登录 &#xff08;2&#xff09;实验室管理 &#xff08;3&#xff09;公告信息…