特斯拉产品研发创新中心 3 月 22 日笔试答案

特斯拉,产品,研发,创新,中心,笔试,答案 · 浏览次数 : 85

小编点评

```python class Solution: def scheduleCourse(self, courses: List[List[int]]) -> int: # 按照截止时间排序 courses.sort(key=lambda x: x[1]) # 选择列表(大顶堆,按照时长降序) heap = PriorityQueue(key=lambda x: x[0]) time = 0 for course in courses: if time + course[0] <= course[1]: # 可以选择 time += course[0] heap.offer(course) else: # 无法选择,尝试淘汰并替换耗时最长任务 time -= heap.poll()[0] time += course[0] heap.offer(course) # 选择列表 return len(heap) ``` **复杂度分析:** * 时间复杂度:$O(nlgn + n)$,其中 $n$ 为 $courses$ 数组的长度。这是因为我们使用的是堆排序,每次添加一个元素需要 $O(log n)$ 的时间,而排序算法的时间复杂度为 $O(nlog n)$。 * 空间复杂度:$O(lgn + n)$,其中 $l$ 是排版后的结果数组的长度,$n$ 是 $courses$ 数组的长度。这是因为我们使用的是堆排序,每次添加一个元素需要 $O(log n)$ 的时间,因此整体的存储空间复杂度为 $O(lgn)$。 **相似题目:** * 55. 跳跃游戏207. 课程表210. 课程表 II252. 会议室253. 会议室 II2402. 会议室 III。

正文

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。

大家好,我是小彭。

昨天是 「特斯拉 2023 春季公开笔试 - 产品研发创新中心专场」,你参加了吗?这场笔试整体来看没有难到特别离谱的题,但需要一些思维。


竞赛题解一览

T1 · 亲密字符串(Easy)

  • 题解:模拟 $O(n + C)$

T2 · 数青蛙(Medium)

  • 题解:模拟 $O(n + C)$

T3 · 复原 IP 地址(Medium)

  • 题解:回溯 $O(3^4·n)$

T4 · 课程表 III(Hard)

  • 题解:贪心 + 大顶堆 $O(nlgn + n)$


T1 · 亲密字符串(Easy)

题目地址

https://leetcode.cn/problems/buddy-strings/

题目描述

给你两个字符串 s 和 goal ,只要我们可以通过交换 s 中的两个字母得到与 goal 相等的结果,就返回 true ;否则返回 false 。

交换字母的定义是:取两个下标 i 和 j (下标从 0 开始)且满足 i != j ,接着交换 s[i] 和 s[j] 处的字符。

  • 例如,在 "abcd" 中交换下标 0 和下标 2 的元素可以生成 "cbad" 。

题解(模拟)

简单模拟题。

  • 如果 sgoal 的长度不同或者词频不同,则必然不符;
  • 如果 sgoal 不相符的位置数量超过 2,则必然不符;
  • 如果 sgoal 不相符的位置数量为 2,则必然相符(因为词频相同,所以可以不用判断这两个位置上的字符是否对立);
  • 如果 sgoal 不相符的位置数量为 1,则必然不符;
  • 如果 sgoal 不相符的位置数量为 0,则需要判断是否至少存在一个字符的词频大于 1。
class Solution {
    fun buddyStrings(s: String, goal: String): Boolean {
        // 长度不同
        if (s.length != goal.length) return false
        // 计数
        var diff = 0
        val cnts1 = IntArray(26)
        val cnts2 = IntArray(26)
        for (index in s.indices) {
            cnts1[s[index] - 'a']++
            cnts2[goal[index] - 'a']++
            // 字符不匹配
            if (s[index] != goal[index]) diff++
        }
        // 检查
        var flag = false
        for (index in cnts1.indices) {
            // 词频不同
            if (cnts1[index] != cnts2[index]) return false
            // 词频大于等于 2
            if (cnts1[index] >= 2) flag = true
        }
        return diff == 2 || (diff == 0 && flag)
    }
}

复杂度分析:

  • 时间复杂度:$O(n + C)$ 其中 $n$ 是 $nums$ 数组的长度,$C$ 是字符集大小,$C$ 为常数 $26$;
  • 空间复杂度:$O(C)$ 计数数组空间。

相似题目:


T2 · 数青蛙(Medium)

题目地址

https://leetcode.cn/problems/minimum-number-of-frogs-croaking/

题目描述

给你一个字符串 croakOfFrogs,它表示不同青蛙发出的蛙鸣声(字符串 "croak" )的组合。由于同一时间可以有多只青蛙呱呱作响,所以 croakOfFrogs 中会混合多个 “croak” 

请你返回模拟字符串中所有蛙鸣所需不同青蛙的最少数目。

要想发出蛙鸣 "croak",青蛙必须 依序 输出 ‘c’, ’r’, ’o’, ’a’, ’k’ 这 5 个字母。如果没有输出全部五个字母,那么它就不会发出声音。如果字符串 croakOfFrogs 不是由若干有效的 "croak" 字符混合而成,请返回 -1 。

题解(模拟)

中等模拟题,这道题卡了很久,浪费很多时间在思考栈、队列、单调队列等方向上。

我们发现:合法的青蛙叫声应该是按照 c → r → o → a → k 的顺序出现的,因此,叫声的每个阶段必然是(非严格)递增的。例如示例 crcaokroak 非法:在处理到第一个 'a' 的位置时,'a' 的计数是 1,'o' 的计数是 0,所以必然不合法。

因此,我们可以维护每个字符的出现次数,在处理每个字符时先累加当前字符的出现次数,再检查上一个阶段的字符的出现次数是否大于等于当前字符的出现次数('c' 不需要检查)。

另外,题目要求的是最多青蛙数量,答案应该记录 'c''k' 字符的最大差值。

class Solution {
    fun minNumberOfFrogs(croakOfFrogs: String): Int {
        // 字符映射到数字
        val ids = mapOf('c' to 0, 'r' to 1, 'o' to 2, 'a' to 3, 'k' to 4)
        var ret = 0
        // 字符计数
        val cnts = IntArray(5)
        // 枚举字符
        for (c in croakOfFrogs) {
            ++cnts[ids[c]!!]
            // 检查上一个阶段的字符是否足够
            if ('c' != c && cnts[ids[c]!! - 1] < cnts[ids[c]!!]) return -1
            // 记录最大差值
            ret = Math.max(ret, cnts[0] - cnts[4])
        }
        // 检查各个阶段出现次数是否相等
        if (!cnts.all { it == cnts[0] }) return -1
        return ret
    }
}

复杂度分析:

  • 时间复杂度:$O(n + C)$ 其中 $n$ 是 $croakOfFrogs$ 字符串的长度,$C$ 是字符集大小,$C$ 为常数 $5$;
  • 空间复杂度:$O(C)$ 计数数组空间。

相似题目:


T3 · 复原 IP 地址(Medium)

题目地址

https://leetcode.cn/problems/restore-ip-addresses/

题目描述

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245""192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

题解(回溯)

简单回溯模板题,按照回溯三要素编码即可:

  • 终止条件:index == s.length
  • 路径 path:已选择的片段,我们用 List 记录,再输出结果时再补充分隔符
  • 选择列表:可以选择 1 到 3 个字符,限制条件是不能有前导 0 且数字大小不超过 255。
class Solution {
    fun restoreIpAddresses(s: String): List<String> {
        val ret = LinkedList<String>()
        backtrack(s, 0, LinkedList<String>(), ret)
        return ret
    }

    private fun backtrack(s: String, index: Int, path: LinkedList<String>, result: LinkedList<String>) {
        // 终止条件
        if (index == s.length) {
            // 满足 IPv4 格式
            if (path.size == 4) result.add(path.joinToString("."))
            return
        }
        // 剪枝:已经达到 4 个片段但字符串未结束
        if (path.size == 4) return
        // 剪枝:1 到 3 个字符,但不能有前导 0 和越界
        val maxIndex = if (s[index] == '0') index else Math.min(index + 2, s.length - 1)
        // 枚举选项
        for (toIndex in index..maxIndex) {
            val segment = s.substring(index, toIndex + 1)
            // 剪枝:超过 255 范围
            if (segment.toInt() > 255) return
            // 选择
            path.add(segment)
            // 递归
            backtrack(s, toIndex + 1, path, result)
            // 回溯
            path.removeLast()
        }
    }
}

复杂度分析:

  • 时间复杂度:$O(3^4·n)$ 其中 3 是每一层的最大选择列表;4 是最大片段数,n 是字符串的长度。回溯递归栈的最大深度是 4 层,每一层有 3 种选项,因此一共有 $3^4$ 种子状态,每个子状态最多需要花费 $O(n)$ 时间构造结果字符串;
  • 空间复杂度:$O(4)$ 递归栈空间,不考虑结果数组和路径数组。

相似题目:


T4 · 课程表 III(Hard)

题目地址

https://leetcode.cn/problems/course-schedule-iii/description/

题目描述

这里有 n 门不同的在线课程,按从 1 到 n 编号。给你一个数组 courses ,其中 courses[i] = [durationi, lastDayi] 表示第 i 门课将会 持续 上 durationi 天课,并且必须在不晚于 lastDayi 的时候完成。

你的学期从第 1 天开始。且不能同时修读两门及两门以上的课程。

返回你最多可以修读的课程数目。

题解(排序 + 贪心 + 大顶堆)

这道题可以用常识辅助思考:

  • 如果两门课的 DDL 不同,那么应该优先学 DDL 较早的;

亦可严格证明:设两门课为 c1(t1, d1), c2(t2, d2),且满足 d1 ≤ d2,即 c1 的截止时间较早,则有 4 种上课方案(设学期开始时间为 0):

  • 只上 c1,需要满足 t1 <= d1
  • 只上 c2:需要满足 t2 <= d2
  • 先上 c1 再上 c2,需要满足 t1 + t2 <= d2
  • 先上 c2 再上 c1,需要满足 t2 + t1 <= d1 <= d2

由于 d1 <= d2,因此 「先学习后者,再学习前者」的条件 t2 + t1 <= d1 成立,也说明 「先学习前者,再学习后者」的条件 t2 + t1 <= d2 也成立。但反过来,如果 t1 + t2 <= d2 无法推出 t2 + t1 <= d1 成立。

以上说明先学习截止时间晚的方案不会比先学习截止时间早的方案更优。因此,我们可以先将所有的课程按照截止时间进行升序排序,再依次挑选课程进行学习。

  • 如果两门课的 DDL 相同,那么应该优先学 Duration 较短的;

亦可用类似的方式证明,也很容易直观理解,优先学习时长更短的课程能够减缓时间推进速度,更有利于选出更优的方案。

  • 如果一门课没有足够的时间完成,那么可以尝试战术性淘汰已选择列表中耗时最长课程,给之后的课程留出时间。

这一点比较不好想到,但是编码不难,难的是如何证明 “淘汰已选择列表中耗时最长课程” 的做法是最优的方案,以及如何保证替换淘汰后替换成当前课程后也能够满足截止时间限制。

亦可简单证明:设已选择的前 i - 1 门课程的最优方案为 {t_1、t_2、t_3、…t_{i-1}} 有学习总时长 time,那么对于课程 courses[i] 来说,则有:

  • 如果 time + courses[i][0] ≤ courses[i][1],那么可以进行学习;
  • 否则,我们从已选择列表中寻找出学习时长最长的课程 courses[j],且满足 courses[j][0] > courses[i][0],即该课程的学习时长大于当前课程的时长。那么我们替换这两个课程(每个课程的贡献都是 1 的前提下),一定不会得到更差的方案,且能够减缓时间的推进进度。

最后剩下的问题是如何寻找 “已选择列表中耗时最长课程”,这个用大顶堆很简单。

class Solution {
    fun scheduleCourse(courses: Array<IntArray>): Int {
        // 按照截止时间排序
        Arrays.sort(courses) { c1, c2 ->
            c1[1] - c2[1]
        }
        // 选择列表(大顶堆,按照时长降序)
        val heap = PriorityQueue<IntArray>() { c1, c2 ->
            c2[0] - c1[0]
        }
        var time = 0
        for (course in courses) {
            if (time + course[0] <= course[1]) {
                // 可以选择
                time += course[0]
                heap.offer(course)
            } else if (!heap.isEmpty() && heap.peek()[0] > course[0]) {
                // 无法选择,尝试淘汰并替换耗时最长任务
                time -= heap.poll()[0]
                time += course[0]
                heap.offer(course)
            } // else 无法替换
        }
        // 选择列表
        return heap.size
    }
}

复杂度分析:

  • 时间复杂度:$O(nlgn + n)$ 其中 $n$ 为 $courses$ 数组的长度;
  • 空间复杂度:$O(lgn + n)$ 排序递归栈空间和大顶堆空间。

相似题目:

与特斯拉产品研发创新中心 3 月 22 日笔试答案相似的内容:

特斯拉产品研发创新中心 3 月 22 日笔试答案

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 昨天是 「特斯拉 2023 春季公开笔试 - 产品研发创新中心专场」,你参加了吗?这场笔试整体来看没有难到特别离谱的题,但需要一些思维。 竞赛题解一览 T1 · 亲密字符串(Easy) 题

华为云GaussDB为MetaERP“成本核算”产品“保驾护航”

摘要:华为宣布实现了自主创新的MetaERP研发,并且完成了对旧ERP系统的全面替换,这其中,就采用了华为云GaussDB数据库特有的全密态技术,对ERP系统中的绝密数据进行加密保护,从而保障了数据的安全。 ERP系统在帮助企业优化业务流程、实现数字化管理方面有重要作用,可以说企业所有的业务流转都需

AI DevOps | ChatGPT 与研发效能、效率提升(中)

为啥 ChatGPT 突然火了? 简单概括就是:产品太过惊艳,体验超预期 之前人工智能发展多年,报道最多的也许就是曾经的李世石大战AlphaGo,现实中的特斯拉自动驾驶,还有波士顿动能放出的机器狗。对于圈外人士来说一般也接触不到这些,仅仅看看而已。但是 ChatGPT 不一样,一声巨响,石头中蹦出一

Go泛型解密:从基础到实战的全方位解析

本篇文章深入探讨了Go语言的泛型特性,从其基础概念到高级用法,并通过实战示例展示了其在实际项目中的应用。 关注【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资

Go语句与表达式深度解析:全案例手册

关注公众号【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。 语句 语句是Go编程语言中完成特定操作的单

TiKV 源码分析之 PointGet

作者:来自 vivo 互联网存储研发团队-Guo Xiang 本文介绍了TiDB中最基本的PointGet算子在存储层TiKV中的执行流程。 一、背景介绍 TiDB是一款具有HTAP能力(同时支持在线事务处理与在线分析处理 )的融合型分布式数据库产品,具备水平扩容或者缩容等重要特性。TiDB 采用多

APP流水线测试领域探索与最佳实践

## 1 背景 APP端UI自动化因其特殊性(需连接测试机)一般都在本地执行,这种执行方式的局限性有以下弊端: 1. 时效性低:研发每次打包后都需要通知测试,测试再去打包平台取包,存在时间差 1. 研发自测或产品验收无法使用自动化脚本:研发自测及产品验收时如果想用自动化脚本需要搭建相应的运行环境并准

数据特征采样在 MySQL 同步一致性校验中的实践

作者:vivo 互联网存储研发团队 - Shang Yongxing 本文介绍了当前DTS应用中,MySQL数据同步使用到的数据一致性校验工具,并对它的实现思路进行分享。 一、背景 在 MySQL 的使用过程中,经常会因为如集群拆分、数据传输、数据聚合等原因产生流动和数据复制。而在通常的数据复制过程

DevOps|破除壁垒,重塑协作-业务闭环释放产研运协作巨大效能

- 会议太多了,员工开会效率降低了50%! 上篇文章《研发效能组织架构:职能独立vs业务闭环》介绍了职能独立型组织架构和业务闭环型组织架构的特点,优劣势。也许有的小伙伴可能对这两种组织架构没有深刻的体会,而本文就是想通过数据说话,想仅仅通过计算这两种组织架构下开会时间这一项,让大家知晓职能型组织架构

[转帖]精华总结:10个问题理解 Linux epoll

epoll 是 linux 特有的一个 I/O 事件通知机制。很久以来对 epoll 如何能够高效处理数以百万记的文件描述符很有兴趣。近期学习、研究了 epoll 源码,在这个过程中关于 epoll 数据结构和作者的实现思路产生出不少疑惑,在此总结为了 10 个问题并逐个加以解答和分析。 本文基于的