当「华为还是备选,迪爹还是迪子」时宇宙厂一面原题

写在前面

alt

2021 年还是互联网元年,当时常规的华为 Offer 还是普遍人的备选,如今的迪爹(BYD)也还是 "来投就给 Offer" 的迪子。

只有字节,当时是公认炙手可热的"宇宙厂"。

作为在 2021 就提前体验了这两年计算机的"供过于求"的字节,自然在算法题上的难度要超出其他大厂招聘的一档。

那么这道「中国互联网鼎盛时期 中的 鼎盛大厂」的算法笔题,现在的你能做出来吗?

题目描述

这是 LeetCode 上的 「1268. 搜索推荐系统」 ,难度为 「中等」

Tag : 「排序」、「字典树」、「哈希表」、「二分」

给你一个产品数组 products 和一个字符串 searchWordproducts 数组中每个产品都是一个字符串。

请你设计一个推荐系统,在依次输入单词 searchWord 的每一个字母后,推荐 products 数组中前缀与 searchWord 相同的最多三个产品。如果前缀相同的可推荐产品超过三个,请按字典序返回最小的三个。

请你以二维列表的形式,返回在输入 searchWord 每个字母后相应的推荐产品的列表。

示例 1:

输入:products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse"
输出:[
["mobile","moneypot","monitor"],
["mobile","moneypot","monitor"],
["mouse","mousepad"],
["mouse","mousepad"],
["mouse","mousepad"]
]

解释:按字典序排序后的产品列表是 ["mobile","moneypot","monitor","mouse","mousepad"]
输入 m 和 mo,由于所有产品的前缀都相同,所以系统返回字典序最小的三个产品 ["mobile","moneypot","monitor"]
输入 mou, mous 和 mouse 后系统都返回 ["mouse","mousepad"]

示例 2:

输入:products = ["havana"], searchWord = "havana"

输出:[["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]

示例 3:

输入:products = ["bags","baggage","banner","box","cloths"], searchWord = "bags"

输出:[["baggage","bags","banner"],["baggage","bags","banner"],["baggage","bags"],["bags"]]

示例 4:

输入:products = ["havana"], searchWord = "tatiana"

输出:[[],[],[],[],[],[],[]]

提示:

  • products[i] 中所有的字符都是小写英文字母。
  • searchWord 中所有字符都是小写英文字母。

排序 + 字典树 + 哈希表

为了方便,将 products 记为 ps,将 searchWord 记为 w

这是一个 "Suggestion string" 问题,容易想到字典树进行求解,不了解字典树的同学,可看 前置 🧀。

由于题目要求「若有超过三个的产品可推荐,返回字典序最小的三个」,我们不妨先对 ps 进行排序,使 ps 从前往后满足字典序从小到大。

「将所有 ps[i] 按顺序添加到字典树 tr 中,添加过程中,使用两个哈希表 minMapmaxMap 分别记录经过某个 tr[i][j] 时的最小 ps 下标和最大 ps 下标」。即哈希表的 key 为具体的 tr[i][j],对应 value 为经过该节点的最小或最大下标。

构建答案时,「根据当前 w 子串到字典树 tr 中查询,定位到该子串对应的 tr[i][j] 为何值,再从哈希表中获取建议字符串在 ps 中的左右端点 lr,」并根据在 ps[l:r] (可能为空集)中最多取三个的原则来构建答案。

考虑实现字典树的两个关键方法,「添加」「查询」

  • 添加函数 void add(String s, int num) :其中 s 为待添加到字典树的字符串,num 则是该字符串在 ps 中的下标编号。

    往字典树添加过程中,按照首次访问字典树节点 tr[i][j] 的下标存入 minMap,最后一次访问字典树节点 tr[i][j] 的下标存入 maxMap 的规则来更新哈希表。

  • 查询函数 int[] query(String s):其中 s 为某个 w 子串,通过查询该子串(最后字符)在字典树的节点值,来得知建议列表对应 ps 的左右端点下标为何值,从而构建答案。

Java 代码:

class Solution {
    int[][] tr = new int[20010][26];
    int idx = 0;
    Map<Integer, Integer> min = new HashMap<>(), max = new HashMap<>();
    void add(String s, int num) {
        int p = 0;
        for (int i = 0; i < s.length(); i++) {
            int u = s.charAt(i) - 'a';
            if (tr[p][u] == 0) {
                tr[p][u] = ++idx;
                min.put(tr[p][u], num);
            }
            max.put(tr[p][u], num);
            p = tr[p][u];
        }
    }
    int[] query(String s) {
        int a = -1, b = -1, p = 0;
        for (int i = 0; i < s.length(); i++) {
            int u = s.charAt(i) - 'a';
            if (tr[p][u] == 0return new int[]{-1, -1};
            a = min.get(tr[p][u]); b = max.get(tr[p][u]);
            p = tr[p][u];
        }
        return new int[]{a, b};
    }
    public List<List<String>> suggestedProducts(String[] ps, String w) {
        Arrays.sort(ps);
        List<List<String>> ans = new ArrayList<>();
        for (int i = 0; i < ps.length; i++) add(ps[i], i);
        for (int i = 0; i < w.length(); i++) {
            List<String> list = new ArrayList<>();
            int[] info = query(w.substring(0, i + 1));
            int l = info[0], r = info[1];
            for (int j = l; j <= Math.min(l + 2, r) && l != -1; j++) list.add(ps[j]);
            ans.add(list);
        }
        return ans;
    }
}

C++ 代码:

class Solution {
public:
    int tr[20010][26] = {0};
    int idx = 0;
    unordered_map<intint> minMap, maxMap;
    void add(string s, int num) {
        int p = 0;
        for (char c : s) {
            int u = c - 'a';
            if (tr[p][u] == 0) {
                tr[p][u] = ++idx;
                minMap[tr[p][u]] = num;
            }
            maxMap[tr[p][u]] = num;
            p = tr[p][u];
        }
    }
    pair<intintquery(string s) {
        int a = -1, b = -1, p = 0;
        for (char c : s) {
            int u = c - 'a';
            if (tr[p][u] == 0return {-1-1};
            a = minMap[tr[p][u]];
            b = maxMap[tr[p][u]];
            p = tr[p][u];
        }
        return {a, b};
    }
    vector<vector<string>> suggestedProducts(vector<string>& ps, string w) {
        sort(ps.begin(), ps.end());
        vector<vector<string>> ans;
        for (int i = 0; i < ps.size(); i++) add(ps[i], i);
        for (int i = 0; i < w.length(); i++) {
            vector<stringlist;
            pair<intint> info = query(w.substr(0, i + 1));
            int l = info.first, r = info.second;
            for (int j = l; j <= min(l + 2, r) && l != -1; j++) list.push_back(ps[j]);
            ans.push_back(list);
        }
        return ans;
    }
};

Python 代码:

class Solution:
    def suggestedProducts(self, ps: List[str], w: str) -> List[List[str]]:
        tr = defaultdict(lambda: defaultdict(int))
        idx = 0
        minMap, maxMap = {}, {}
        def add(s, num):
            nonlocal idx
            p = 0
            for c in s:
                u = ord(c) - ord('a')
                if tr[p][u] == 0:
                    idx += 1
                    tr[p][u] = idx
                    minMap[tr[p][u]] = num
                maxMap[tr[p][u]] = num
                p = tr[p][u]
        def query(s):
            a, b, p = -1-10
            for c in s:
                u = ord(c) - ord('a')
                if tr[p][u] == 0:
                    return (-1-1)
                a = minMap[tr[p][u]]
                b = maxMap[tr[p][u]]
                p = tr[p][u]
            return (a, b)
        ps.sort()
        ans = []
        for i in range(len(ps)):
            add(ps[i], i)
        for i in range(len(w)):
            l, r = query(w[:i + 1])
            lst = [ps[j] for j in range(l, min(l + 3, r + 1)) if l != -1]
            ans.append(lst)
        return ans

TypeScript 代码:

let tr: number[][];
let idx: number;
let minMap: Map<numbernumber>, maxMap: Map<numbernumber>;
function add(s: string, num: number): void {
    let p = 0;
    for (let i = 0; i < s.length; i++) {
        const u = s.charCodeAt(i) - 'a'.charCodeAt(0);
        if (tr[p][u] === 0) {
            idx++;
            tr[p][u] = idx;
            minMap.set(tr[p][u], num);
        }
        maxMap.set(tr[p][u], num);
        p = tr[p][u];
    }
}
function query(s: string): number[] {
    let a = -1, b = -1, p = 0;
    for (let i = 0; i < s.length; i++) {
        const u = s.charCodeAt(i) - 'a'.charCodeAt(0);
        if (tr[p][u] === 0return [-1-1];
        a = minMap.get(tr[p][u])!;
        b = maxMap.get(tr[p][u])!;
        p = tr[p][u];
    }
    return [a, b];
}
function suggestedProducts(ps: string[], w: string): string[][] {
    tr = new Array(20010).fill(0).map(() => new Array(26).fill(0));
    idx = 0;
    minMap = new Map(), maxMap = new Map();
    ps.sort();
    const ans = [];
    for (let i = 0; i < ps.length; i++) add(ps[i], i);
    for (let i = 0; i < w.length; i++) {
        const list = [];
        const [l, r] = query(w.substring(0, i + 1));
        for (let j = l; j <= Math.min(l + 2, r) && l !== -1; j++) list.push(ps[j]);
        ans.push(list);
    }
    return ans;
};
  • 时间复杂度:将 ps 长度记为 nw 长度记为 m。对 ps 进行排序复杂度为 ;构建字典树复杂度为 ;根据 w 构建答案复杂度为 ;整体复杂度为
  • 空间复杂度: ,其中 为字符集大小

排序 + 二分

由于每个 w 子串只会对应最多三个的建议字符串,同时又可以先通过排序来确保 ps 的有序性。

因此对于每个 w 子串而言,可以 「先找到满足要求的,字典序最小的建议字符串 ps[i],接着往后逐个检查,组成最终的建议字符串列表(最多检查三个)」

这个「在 ps 中找符合要求,字典序最小的建议字符串」操作,除了能够利用上述解法来做(省掉一个 maxMap)以外,还能利用字符串本身的有序性进行「二分」,因为该操作本质上,是在「找第一个满足 ps[i] 大于等于当前子串的位置」

Java 代码:

class Solution {
    public List<List<String>> suggestedProducts(String[] ps, String w) {
        Arrays.sort(ps);
        int n = ps.length;
        List<List<String>> ans = new ArrayList<>();
        for (int i = 0; i < w.length(); i++) {
            String cur = w.substring(0, i + 1);
            int l = 0, r = n - 1;
            while (l < r) {
                int mid = l + r >> 1;
                if (ps[mid].compareTo(cur) >= 0) r = mid;
                else l = mid + 1;
            }
            List<String> list = new ArrayList<>();
            if (ps[r].compareTo(cur) >= 0) {
                for (int j = r; j <= Math.min(n - 1, r + 2); j++) {
                    if (ps[j].length() < cur.length()) break;
                    if (!ps[j].substring(0, i + 1).equals(cur)) break;
                    list.add(ps[j]);
                }
            }
            ans.add(list);
        }
        return ans;
    }
}

C++ 代码:

class Solution {
public:
    vector<vector<string>> suggestedProducts(vector<string>& ps, string w) {
        sort(ps.begin(), ps.end());
        int n = ps.size();
        vector<vector<string>> ans;
        for (int i = 0; i < w.length(); i++) {
            string cur = w.substr(0, i + 1);
            int l = 0, r = n - 1;
            while (l < r) {
                int mid = (l + r) >> 1;
                if (ps[mid].compare(cur) >= 0) r = mid;
                else l = mid + 1;
            }
            vector<stringlist;
            if (ps[r].compare(cur) >= 0) {
                for (int j = r; j <= min(n - 1, r + 2); j++) {
                    if (ps[j].length() < cur.length()) break;
                    if (ps[j].substr(0, i + 1) != cur) break;
                    list.push_back(ps[j]);
                }
            }
            ans.push_back(list);
        }
        return ans;
    }
};

Python 代码:

class Solution:
    def suggestedProducts(self, ps: List[str], w: str) -> List[List[str]]:
        ps.sort()
        n = len(ps)
        ans = []
        for i in range(len(w)):
            cur = w[:i + 1]
            l, r = 0, n - 1
            while l < r:
                mid = (l + r) // 2
                if ps[mid] >= cur:
                    r = mid
                else:
                    l = mid + 1
            lst = []
            if ps[r] >= cur:
                for j in range(r, min(n - 1, r + 2) + 1):
                    if len(ps[j]) < len(cur) or ps[j][:i + 1] != cur:
                        break
                    lst.append(ps[j])
            ans.append(lst)
        return ans

TypeScript 代码:

function suggestedProducts(ps: string[], w: string): string[][] {
    ps.sort();
    const n = ps.length;
    const ans = [];
    for (let i = 0; i < w.length; i++) {
        const cur = w.substring(0, i + 1);
        let l = 0, r = n - 1;
        while (l < r) {
            const mid = (l + r) >> 1;
            if (ps[mid].localeCompare(cur) >= 0) r = mid;    
            else l = mid + 1;
        }
        const list: string[] = [];
        if (ps[r].localeCompare(cur) >= 0) {
            for (let j = r; j <= Math.min(n - 1, r + 2); j++) {
                if (ps[j].length < cur.length || !ps[j].startsWith(cur)) break;
                list.push(ps[j]);
            }
        }
        ans.push(list);
    }
    return ans;
};
  • 时间复杂度:将 ps 长度记为 nw 长度记为 m。对 ps 进行排序复杂度为 ;每次二分需要进行字符串比较,复杂度为 ;二分到左端点后需要往后检查最多三个字符串,复杂度为 。整体复杂度为
  • 空间复杂度:

最后

这是我们「刷穿 LeetCode」系列文章的第 No.1268 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

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

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

相关文章

基于helm的方式在k8s集群中部署gitlab - 备份恢复(二)

接上一篇 基于helm的方式在k8s集群中部署gitlab - 部署&#xff08;一&#xff09;&#xff0c;本篇重点介绍在k8s集群中备份gitlab的数据&#xff0c;并在虚拟机上部署相同版本的gitlab&#xff0c;然后将备份的数据进行还原恢复 文章目录 1. 备份2. 恢复到虚拟机上的gitlab2.…

加载minio中存储的静态文件html,不显示样式与js

问题描述:点击链接获取的就是纯静态文件,但是通过浏览器可以看到明明加载了css文件与js文件 原因:仔细看你会发现加载css文件显示的contentType:text/html文件,原来是minio上传文件时将所有文件的contentType设置成了text/html 要在上传时指定文件,根据文章的类型指定的Conten…

鸿蒙开发-ArkTS 语言-循环渲染

鸿蒙开发-ArkTS 语言-状态管理 4. 渲染控制 对于 UI 渲染&#xff0c;可以基于数据结构选择一些内置方法&#xff08;例如&#xff1a;ForEach&#xff09;快速渲染 UI 结构。 4.1 if-else条件渲染 ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态&#xff0c;使…

第十三章 深度解读预训练与微调迁移,模型冻结与解冻(工具)

一个完整的代码 pythonCopy codeimport torch import torchvision import torchvision.transforms as transforms import torch.nn as nn import torch.optim as optim # 设置设备&#xff08;CPU或GPU&#xff09; device torch.device("cuda" if torch.cuda.is_a…

网络运维与网络安全 学习笔记2023.11.26

网络运维与网络安全 学习笔记 第二十七天 今日目标 NAT场景与原理、静态NAT、动态NAT PAT原理与配置、动态PAT之EasyIP、静态PAT之NAT Server NAT场景与原理 项目背景 为节省IP地址和费用&#xff0c;企业内网使用的都是“私有IP地址” Internet网络的组成设备&#xff0c…

终端移动性管理

联系前面所学的知识我们知道&#xff0c;移动性管理主要分为两大类&#xff1a;空闲状态下的移动性管理、连接状态下的移动性管理。我们今天来详细了解他们的工作原理~ 目录 移动性管理分类 1、空闲状态下的移动性管理 2、连接状态下的移动性管理 手机选择天线的原则 4G天…

vue项目npm install报错Failed at the fibersa4.0.3 install script

报错如下 解决&#xff1a;降低node版本 降到12.16.0 参考链接

SSM图书捐赠网站系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 图书捐赠网站系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库 &#xff0c;系统主要采用B/…

BigDecimal的使用全面总结

BigDecimal BigDecimal可以表示任意大小&#xff0c;任意精度的有符号十进制数。所以不用怕精度问题&#xff0c;也不用怕大小问题&#xff0c;放心使用就行了。就是要注意的是&#xff0c;使用的时候有一些注意点。还有就是要注意避免创建的时候存在精度问题&#xff0c;尤其…

LeetCode Hot100 124.二叉树中的最大路径和

题目&#xff1a; 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点…

一起学docker系列之十docker安装tomcat实践

目录 前言1 安装tomcat的步骤步骤 1: 查找并拉取 Tomcat 镜像步骤 2: 运行 Tomcat 容器步骤 3: 管理 Tomcat 容器步骤 4: 访问 Tomcat 首页 2 解决访问首页的404访问不到的问题2.1 Tomcat 10 的默认设置问题2.2 端口映射或防火墙问题 3 推荐使用 Tomcat 8.5 版本总结 前言 当安…

玉渊谭天对电影色彩分析的“蚊香图”复现-python

视频教程链接&#xff1a;https://www.bilibili.com/video/BV1Lu4y1t7FG/ 最终的实现效果如下&#xff1a; 前几天刷抖音刷到了玉渊谭天对于电影抽取画面制作“蚊香图”&#xff0c;相关视频片段如下。 这种制作”蚊香图“的特效当时有点触动到到我&#xff0c;根据色彩来分…

【服务器能干什么】搭建一个短网址平台,可以查看数据详情!

昨天在 YouTube 上看到又一个搭建自己短网址的视频教程&#xff0c;用的是开源的 polr&#xff0c;但是按照步骤一步步搭建下来&#xff0c;最后一步都会出现 顺哥轻创 PLAINTEXT Whoops, looks like something went wrong百度、谷歌查了一圈也没找到有效的解决方法。&#x…

qt pdf 模块简介

文章目录 1. 技术平台2. Qt pdf 模块3. cmake 使用模块4. 许可证5. 简单示例5.1 CMakeLists.txt5.2 main.cpp 6. 总结 1. 技术平台 项目说明OSwin10 x64Qt6.6compilermsvc2022构建工具cmake 2. Qt pdf 模块 Qt PDF模块包含用于呈现PDF文档的类和函数。 QPdfDocument 类加载P…

浏览器中实现可视化的方式有哪几种?带你盘点一下

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热衷分享有趣实用的文章&#xff0c;希望大家多多支持&#xff0c;一起进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 可视化的含义 浏览器中实现可视化的4种方式 1. HTMLCSS 2. SVG …

Java小游戏 王者荣耀

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayLis…

霍夫丁不等式(Hoeffding‘s inequality)

参考资料&#xff1a;Hoeffdings inequality | encyclopedia article by TheFreeDictionary 霍夫丁不等式&#xff08;Hoeffdings inequality&#xff09;描述了随机变量的和、与和的期望之差的上限&#xff1b;或者表述为&#xff1a;随机变量的均值、与均值的期望之差的上限。…

Node.js与npm的准备与操作

1.下载 Node.js官网&#xff1a;Node.jsNode.js is a JavaScript runtime built on Chromes V8 JavaScript engine.https://nodejs.org/en 打开后的界面如下&#xff1a; LTS&#xff08;Long Term Support&#xff09;&#xff1a;长期支持版&#xff0c;稳定版 Current&am…

IDEA出现cannot download sources解决方案

IDEA出现cannot download sources解决方案 问题描述 当我想看第三方库的源码的注释时需要下载源码。 点击Dodnload Sources后可能会出现cannot download sources的问题。 解决方案 这时我们只需在根目录下打开Terminal后执行下面一行代码 mvn dependency:resolve -Dclassi…

【element优化经验】el-dialog修改title样式

目录 前言 解决之路 1.把默认的这个图标隐藏&#xff0c;官方的api有这个属性&#xff1a;showClose值设置false. 2.title插槽定制&#xff1a;左边定制标题&#xff0c;右边定制按钮区域。 3.背景颜色修改&#xff1a;默认title是有padding的需要把它重写调&#xff0c;然…