【面试高频题】难度 3/5,字典树热门运用题
题目描述
这是 LeetCode 上的 「745. 前缀和后缀搜索」 ,难度为 「困难」。
Tag : 「字典树」
设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。
实现 WordFilter
类:
-
WordFilter(string[] words)
使用词典中的单词words
初始化对象。 -
f(string pref, string suff)
返回词典中具有前缀prefix
和后缀suff
的单词的下标。如果存在不止一个满足要求的下标,返回其中 最大的下标 。如果不存在这样的单词,返回 。
示例:
输入
["WordFilter", "f"]
[[["apple"]], ["a", "e"]]
输出
[null, 0]
解释
WordFilter wordFilter = new WordFilter(["apple"]);
wordFilter.f("a", "e"); // 返回 0 ,因为下标为 0 的单词:前缀 prefix = "a" 且 后缀 suff = "e" 。
提示:
-
-
-
-
words[i]
、pref
和suff
仅由小写英文字母组成 -
最多对函数 f
执行 次调用
基本分析
为了方便,我们令 words
为 ss
,令 pref
和 suff
分别为 a
和 b
。
搜索某个前缀(后缀可看做是反方向的前缀)容易想到字典树,但单词长度数据范围只有 ,十分具有迷惑性,使用暴力做法最坏情况下会扫描所有的 ,不考虑任何的剪枝操作的话,计算量也才为 ,按道理是完全可以过的。
但不要忘记 LC 是一个具有「设定每个样例时长,同时又有总时长」这样奇怪机制的 OJ。
暴力(TLE or 双百)
于是有了 Java
总时间超时,TypeScripe
双百的结果(应该是 TypeScript
提交不多,同时设限宽松的原因):
Java 代码:
class WordFilter {
String[] ss;
public WordFilter(String[] words) {
ss = words;
}
public int f(String a, String b) {
int n = a.length(), m = b.length();
for (int k = ss.length - 1; k >= 0; k--) {
String cur = ss[k];
int len = cur.length();
if (len < n || len < m) continue;
boolean ok = true;
for (int i = 0; i < n && ok; i++) {
if (cur.charAt(i) != a.charAt(i)) ok = false;
}
for (int i = 0; i < m && ok; i++) {
if (cur.charAt(len - 1 - i) != b.charAt(m - 1 - i)) ok = false;
}
if (ok) return k;
}
return -1;
}
}
TypeScript 代码:
class WordFilter {
ss: string[]
constructor(words: string[]) {
this.ss = words
}
f(a: string, b: string): number {
const n = a.length, m = b.length
for (let k = this.ss.length - 1; k >= 0; k--) {
const cur = this.ss[k]
const len = cur.length
if (len < n || len < m) continue
let ok = true
for (let i = 0; i < n && ok; i++) {
if (cur[i] != a[i]) ok = false
}
for (let i = m - 1; i >= 0; i--) {
if (cur[len - 1 - i] != b[m - 1 - i]) ok = false
}
if (ok) return k
}
return -1
}
}
-
时间复杂度:初始化操作复杂度为 ,检索操作复杂度为 -
空间复杂度:
Trie
使用字典树优化检索过程也是容易的,分别使用两棵 Trie
树来记录 的前后缀,即正着存到 tr1
中,反着存到 Tr2
中。
❝还不了解
❞Trie
的同学可以先看前置 🧀:实现 Trie (前缀树) 前置 🧀 通过图解形式讲解了Trie
的结构与原理,以及提供了两种实现Trie
的方式
同时对于字典树的每个节点,我们使用数组 idxs
记录经过该节点的字符串 所在 ss
中的下标 ,若某个字典树节点的索引数组 tr.idxs
为 则代表「从根节点到 tr
节点所对应的字符串」为 的前缀。
这样我们可以即可在扫描前后缀 a
和 b
时,得到对应的候选下标列表 l1
和 l2
,由于我们将 添加到两棵 tr
中是按照下标「从小到大」进行,因此我们使用「双指针」算法分别从 l1
和 l2
结尾往后找到第一个共同元素即是答案(满足条件的最大下标)。
❝使用
❞Trie
优化后,Java
从TLE
到AC
,TypeScript
耗时为原本的 :
Java 代码:
class WordFilter {
class TrieNode {
TrieNode[] tns = new TrieNode[26];
List<Integer> idxs = new ArrayList<>();
}
void add(TrieNode p, String s, int idx, boolean isTurn) {
int n = s.length();
p.idxs.add(idx);
for (int i = isTurn ? n - 1 : 0; i >= 0 && i < n; i += isTurn ? -1 : 1) {
int u = s.charAt(i) - 'a';
if (p.tns[u] == null) p.tns[u] = new TrieNode();
p = p.tns[u];
p.idxs.add(idx);
}
}
int query(String a, String b) {
int n = a.length(), m = b.length();
TrieNode p = tr1;
for (int i = 0; i < n; i++) {
int u = a.charAt(i) - 'a';
if (p.tns[u] == null) return -1;
p = p.tns[u];
}
List<Integer> l1 = p.idxs;
p = tr2;
for (int i = m - 1; i >= 0; i--) {
int u = b.charAt(i) - 'a';
if (p.tns[u] == null) return -1;
p = p.tns[u];
}
List<Integer> l2 = p.idxs;
n = l1.size(); m = l2.size();
for (int i = n - 1, j = m - 1; i >= 0 && j >= 0; ) {
if (l1.get(i) > l2.get(j)) i--;
else if (l1.get(i) < l2.get(j)) j--;
else return l1.get(i);
}
return -1;
}
TrieNode tr1 = new TrieNode(), tr2 = new TrieNode();
public WordFilter(String[] ss) {
int n = ss.length;
for (int i = 0; i < n; i++) {
add(tr1, ss[i], i, false);
add(tr2, ss[i], i, true);
}
}
public int f(String a, String b) {
return query(a, b);
}
}
TypeScript 代码:
class TrieNode {
tns: TrieNode[] = new Array<TrieNode>()
idxs: number[] = new Array<number>()
}
class WordFilter {
add(p: TrieNode, s: string, idx: number, isTurn: boolean): void {
const n = s.length
p.idxs.push(idx)
for (let i = isTurn ? n - 1 : 0; i >= 0 && i < n; i += isTurn ? -1 : 1) {
const u = s.charCodeAt(i) - 'a'.charCodeAt(0)
if (p.tns[u] == null) p.tns[u] = new TrieNode()
p = p.tns[u]
p.idxs.push(idx)
}
}
query(a: string, b: string): number {
let n = a.length, m = b.length
let p = this.tr1
for (let i = 0; i < n; i++) {
const u = a.charCodeAt(i) - 'a'.charCodeAt(0)
if (p.tns[u] == null) return -1
p = p.tns[u]
}
const l1 = p.idxs
p = this.tr2
for (let i = m - 1; i >= 0; i--) {
const u = b.charCodeAt(i) - 'a'.charCodeAt(0)
if (p.tns[u] == null) return -1
p = p.tns[u]
}
const l2 = p.idxs
n = l1.length; m = l2.length
for (let i = n - 1, j = m - 1; i >= 0 && j >= 0; ) {
if (l1[i] < l2[j]) j--
else if (l1[i] > l2[j]) i--
else return l1[i]
}
return -1
}
tr1: TrieNode = new TrieNode()
tr2: TrieNode = new TrieNode()
constructor(ss: string[]) {
for (let i = 0; i < ss.length; i++) {
this.add(this.tr1, ss[i], i, false)
this.add(this.tr2, ss[i], i, true)
}
}
f(a: string, b: string): number {
return this.query(a, b)
}
}
C++ 代码:
class WordFilter {
public:
struct TrieNode {
TrieNode* tns[26] {nullptr};
vector<int> idxs;
};
void add(TrieNode* p, const string& s, int idx, bool isTurn) {
int n = s.size();
p->idxs.push_back(idx);
for(int i = isTurn ? n - 1 : 0; i >= 0 && i < n; i += isTurn ? -1 : 1) {
int u = s[i] - 'a';
if(p->tns[u] == nullptr) p->tns[u] = new TrieNode();
p = p->tns[u];
p->idxs.push_back(idx);
}
}
int query(const string& a, const string& b) {
int n = a.size(), m = b.size();
auto p = tr1;
for(int i = 0; i < n; i++) {
int u = a[i] - 'a';
if(p->tns[u] == nullptr) return -1;
p = p->tns[u];
}
vector<int>& l1 = p->idxs;
p = tr2;
for(int i = m - 1; i >= 0; i--) {
int u = b[i] - 'a';
if(p->tns[u] == nullptr) return -1;
p = p->tns[u];
}
vector<int>& l2 = p->idxs;
n = l1.size(), m = l2.size();
for(int i = n - 1, j = m - 1; i >= 0 && j >= 0; ) {
if(l1[i] > l2[j]) i--;
else if(l1[i] < l2[j]) j--;
else return l1[i];
}
return -1;
}
TrieNode* tr1 = new TrieNode, *tr2 = new TrieNode;
WordFilter(vector<string>& ss) {
int n = ss.size();
for(int i = 0; i < n; i++) {
add(tr1, ss[i], i, false);
add(tr2, ss[i], i, true);
}
}
int f(string a, string b) {
return query(a, b);
}
};
-
时间复杂度:初始化操作复杂度为 ,检索过程复杂度为 ,其中 为前后缀的最大长度, 为初始化数组长度,代表最多有 个候选下标(注意:这里的 只是粗略分析,实际上如果候选集长度越大的话,说明两个候选集是重合度是越高的,从后往前找的过程是越快结束的,可以通过方程算出一个双指针的理论最大比较次数 ,如果要将 卡满成 的话,需要将两个候选集设计成交替下标,此时 f
如果仍是 次调用的话,必然会面临大量的重复查询,可通过引入map
记录查询次数来进行优化) -
空间复杂度:
最后
这是我们「刷穿 LeetCode」系列文章的第 No.745
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉
相关文章:
【面试高频题】难度 3/5,字典树热门运用题
题目描述 这是 LeetCode 上的 「745. 前缀和后缀搜索」 ,难度为 「困难」。 Tag : 「字典树」 设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。 实现 WordFilter 类: WordFilter(string[] words) 使用词典中的单词 words 初…...
vue base64图片转file流 下载到本地 或者上传
<img :src"data:image/png;base64,form.img" style"max-width:280px;max-height: 280px;margin: auto;" />// base64 转file const base64ToFile()>{let byImg atob(form.img); // 解码base64let n byImg.lengthlet a new Uint8Array(n);while…...
无涯教程-PHP - 简介
PHP 7是最期待的,它是PHP编程语言的主要功能版本。 PHP 7于2015年12月3日发布。本教程将以简单直观的方式教您PHP 7的新功能及其用法。 无涯教程假设您已经了解旧版本的PHP,现在就可以开始学习PHP 7的新功能。 使用下面的示例- <html><head&…...
web基础+HTTP协议+httpd详细配置
目目录录 一、Web基础1.1 HTML概述1.1.1 HTML的文件结构1.1.2 HTML中的部分基本标签 1.3 MIME1.4 URI 和 URL1.4 定义1.4.2 URI 和 URL 的区别 二、静态资源和动态资源2.1 静态资源2.2 动态资源 三、HTTP协议3.1 HTTP协议简介3.2 HTTP协议版本3.2 HTTP方法3.3 HTTP请求访问的完…...
【sql】MongoDB的增删改查分页条件等
【sql】MongoDB的增删改查分页条件等 //增 //新增数据2种方式 db.msg.save({"name":"springboot😀"}); db.msg.insert({"name":"mango good"}); db.msg.save({"name":"springboot",type:"工具书&…...
我的动态归纳(便于搜索)
linux dns配置文件是“/etc/resolv.conf”,该配置文件用于配置DNS客户,它包含了主机的域名搜索顺序和DNS/服务器的地址,每一行包括一个关键字和一个或多个空格隔开的参数。 /etc/resolv.conf (不配置就不能域名解析) 可…...
langchain ChatGPT AI私有知识库
企业知识库 原理就是把文档变为向量数据库,然后搜索向量数据库,把相似的数据和问题作为prompt, 输入到大模型,再利用GPT强大的自然语言处理、推理和分析等方面的能力将答案返回给用户 什么是langchain? langchain是一个强大的…...
API接口常用数据格式Json,Json的定义和XML的区别
现在程序员还有谁不知道 JSON 吗?无论对于前端还是后端,JSON 都是一种常见的数据格式。那么 JSON 到底是什么呢? JSON 的定义 JSON (JavaScript Object Notation) ,是一种轻量级的数据交换格式。它的使用…...
密码学学习笔记(二十一):SHA-256与HMAC、NMAC、KMAC
SHA-256 SHA-2是广泛应用的哈希函数,并且有不同的版本,这篇博客主要介绍SHA-256。 SHA-256算法满足了哈希函数的三个安全属性: 抗第一原像性 - 无法根据哈希函数的输出恢复其对应的输入。抗第二原像性 - 给定一个输入和它的哈希值…...
操作系统-笔记-第四章-文件管理
目录 四、第四章——文件管理 1、文件管理——基础概念 (1)文件结构 (2)操作系统提供的接口 (3)总结 2、文件的逻辑结构 (1)有结构文件(类似SQL表文件)…...
【MiniGUI】文字颜色实现透明度变化
在MiniGUi中,输出文字时有时候希望文字带有透明度信息, 即文字能够透出下面的图像来。 很自然地想到,设置颜色时,将颜色设置为带有透明度的颜色: SelectFont(hdc, mg_font);SetTextColor(hdc, RGBA2Pixel(HDC_SCREEN, …...
css中元素加定位之后到一定距离元素会变小
css中元素加定位之后到一定距离元素会变小 主要原因:元素没有加宽高 .swiperWrapper .active{bottom: 380px;left: 215px;z-index: 10; } .swiperWrapper .next{bottom: 170px;left: 7%;z-index: 20; } .swiperWrapper .prev{bottom: 360px;left: 0%;z-index: 30;…...
Java 语言实现冒泡排序
Java 语言实现冒泡排序 介绍 冒泡排序是一种简单直观的排序算法,它重复地比较相邻的两个元素,如果顺序错误就交换它们,直到没有需要交换的元素为止。冒泡排序的思路是通过每一轮的比较将最大(或最小)的元素逐渐“冒泡…...
面向对象单选题
下列选项中不属于面向对象的特征的是(B) A、封装性 B、安全性 C、继承性 D、多态性 在Java中,关于继承,类只支持(A) A、单继承 B、多继承 C、两个都可以 D、两个都不可以 用于定义成员的访问控制权的一组关键字…...
微服务-Fegin
在之前我们两服务之间调用的时候用的是restTemplate,但是这个方式调用存在很多的问题 String url "http://userservice/user/" order.getUserId(); 代码可读性差,编码体验不统一参数复杂的url难以维护 所以我们大力推出我们今天的主角--Fegin Feign是…...
[oneAPI] 使用字符级 RNN 生成名称
[oneAPI] 使用字符级 RNN 生成名称 oneAPI特殊写法使用字符级 RNN 生成名称Intel Optimization for PyTorch数据下载加载数据并对数据进行处理创建网络训练过程准备训练训练网络 结果 参考资料 比赛:https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517…...
【ROS】参数服务器--理论模型与参数操作(C++)
一、概念介绍 参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。 作用:存储一些多节点…...
[oneAPI] 基于BERT预训练模型的英文文本蕴含任务
[oneAPI] 基于BERT预训练模型的英文文本蕴含任务 Intel DevCloud for oneAPI 和 Intel Optimization for PyTorch基于BERT预训练模型的英文文本蕴含任务语料介绍数据集构建 模型训练 结果参考资料 比赛:https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0…...
【洛谷】P1163 银行贷款
原题链接:https://www.luogu.com.cn/problem/P1163 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 这题需要注意的是利率按月累计这句话,也就是相当于“利滚利”。 我们定义sum变量表示贷款原值,money表示每月支付…...
Java版工程行业管理系统源码-专业的工程管理软件-提供一站式服务 em
鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工…...
kafka--技术文档--基本docker中安装<单机>-linux
安装zookeeper 阿丹小科普: Kafka在0.11.0.0版本之后不再依赖Zookeeper,而是使用基于Raft协议的Kafka自身的仲裁机制来替代Zookeeper。具体来说,Kafka 2.8.0版本是第一个不需要Zookeeper就可以运行Kafka的版本,这被称为Kafka Raf…...
回归预测 | MATLAB实现WOA-RF鲸鱼优化算法优化随机森林算法多输入单输出回归预测(多指标,多图)
回归预测 | MATLAB实现WOA-RF鲸鱼优化算法优化随机森林算法多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现WOA-RF鲸鱼优化算法优化随机森林算法多输入单输出回归预测(多指标,多图)效果一览…...
Linux系统安全——NAT(SNAT、DNAT)
目录 NAT SNAT SNAT实际操作 DNAT DNAT实际操作 NAT NAT: network address translation,支持PREROUTING,INPUT,OUTPUT,POSTROUTING四个链 请求报文:修改源/目标IP, 响应报文:修改源/目标…...
uniapp项目添加人脸识别功能,可用作登录,付款,流程审批前的安全校验
本案例使用了hbuilder插件商城中的活体检验插件,可自行前往作者处下载查看, 效果图如下 此插件需要在manifest.json中勾选 实现流程 1:前往hbuilder插件市场下载插件 2:在页面中导入import face from "/uni_modules/mcc-…...
SpringBoot面试题
Spring Boot的启动流程主要分为以下几个步骤: 加载Spring Boot配置文件,初始化Spring Boot环境和核心组件,如ApplicationContext上下文环境、自动装配机制等。 执行SpringApplication.run()方法,执行所有Spring Boot自动配置的Be…...
Git相关命令
SSH密钥文件 Github里面S设置SH公钥有两者选择方式 账号下的每个仓库都设置一个公钥,因为GitHub官方要求每个仓库的公钥都不能相同,所以每个账号都要搞一个密钥(很麻烦)给账号分配一个公钥,然后这个公钥就可以在这个…...
《HeadFirst设计模式(第二版)》第八章代码——模板方法模式
代码文件目录: CaffeineBeverage package Chapter8_TemplateMethodPattern;/*** Author 竹心* Date 2023/8/17**/public abstract class CaffeineBeverage {final void prepareRecipe(){boilWater();brew();pourInCup();//这里使用钩子customerWantsCondiments()来…...
RESTful API,以及如何使用它构建 web 应用程序
RESTful API是一种基于HTTP协议的API设计风格,它的核心思想是将资源作为 API 的核心,使用 HTTP 的 GET、POST、PUT、DELETE 等方法对这些资源进行操作,并通过 URL 来定位资源。 RESTful API的特点包括: 资源是 API 的核心使用 H…...
Git+Gitee使用分享
GitGitee快速入门 创建仓库 初始化本地仓库 验证本地git是否安装好 打开cmd窗口,输入git 这样就OK。 Git 全局设置:(只需要设置一次) 这台电脑如果是第一次使用git,就需要这样初始化一下,这样才知道是谁提交到仓库了。 git confi…...
【3D激光SLAM】LOAM源代码解析--transformMaintenance.cpp
系列文章目录 【3D激光SLAM】LOAM源代码解析–scanRegistration.cpp 【3D激光SLAM】LOAM源代码解析–laserOdometry.cpp 【3D激光SLAM】LOAM源代码解析–laserMapping.cpp 【3D激光SLAM】LOAM源代码解析–transformMaintenance.cpp 写在前面 本系列文章将对LOAM源代码进行讲解…...
网络推广品牌营销公司/appstore关键词优化
目录 基础资料 原项目地址 实现功能: 硬件准备 软件版本 固件版本 补充资料 硬件准备 遥控小车车身拼装 2.1 小车车身组装 2.2接线图示 烧录小车源码 待完成 基础资料 基于Air780E开发板:Air780E文档中心 原项目地址 luatos-airtun: Air…...
wordpress max upload/有没有好用的网站推荐
一般读取文件我们用fopen 或者 file_get_contents ,前者可以循环读取,后者可以一次性读取,但都是将文件内容一次性加载来操作。 如果加载的文件特别大时,如几百M,上G时,这时性能就降下来了,那么PHP里有没有…...
网站建设要多少费用/网络推广免费网站
要想全面了解java开发工具,我们首先需要先了解一下java程序的开发过程,通过这个过程我们能够了解到java开发都需要用到那些工具。 首先我们先了解完整项目开发过程,如图所示: 从上图中我们能看到一个完整的java项目的开发包括很多…...
wordpress默认用某一号字体/百度客服中心
1011 AB 和 C (15 分) 给定区间 [−231,231] 内的 3 个整数 A、B 和 C,请判断 AB 是否大于 C。 输入格式: 输入第 1 行给出正整数 T (≤10),是测试用例的个数。随后给出 T 组测试用例,每组占…...
西安十强互联网站建设公司/软文怎么写比较吸引人
目录 一、实验拓扑 二、实验步骤 三、实验过程 总结 实验难度2实验复杂度3一、实验拓扑 二、实验步骤 1.搭建如图所示的网络拓扑图; 2.初始化路由器设备,配置IP地址; 3.配置ASA的初始相关参数: 防火墙的名称为ASA 接口接…...
dw网页设计软件的学习网站/广点通和腾讯朋友圈广告区别
最近在玩Kinect,使用的是Unity,发现网上好像没有什么教程。自己就只有抱着英文版帮助文档啃,真是苦逼 本人英语也不好,大家将就着看吧 Kinect入门帮助 如何运行示例 1 下载并安装Kinect v2 SDK,详情将在下一节中…...