LeetCode 周赛上分之旅 #35 两题坐牢,菜鸡现出原形
️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问。
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。
本文是 LeetCode 上分之旅系列的第 35 篇文章,往期回顾请移步到文章末尾~
T1. 按分隔符拆分字符串(Easy)
- 标签:模拟
T2. 合并后数组中的最大元素(Medium)
- 标签:贪心
T3. 长度递增组的最大数目(Hard)
- 标签:排序、贪心
T4. 树中可以形成回文的路径数(Hard)
- 标签:状态压缩、前缀和、散列表

T1. 按分隔符拆分字符串(Easy)
https://leetcode.cn/problems/split-strings-by-separator/
题解(模拟)
简单模拟题。
class Solution:
def splitWordsBySeparator(self, words: List[str], separator: str) -> List[str]:
ans = []
for x in words:
for y in x.split(separator):
if y != "":
ans.append(y)
return ans
复杂度分析:
- 时间复杂度:$O(L)$ 其中 $L$ 为字符总数;
- 空间复杂度:$O(1)$ 不考虑结果数组,仅占用常量级别空间。
T2. 合并后数组中的最大元素(Medium)
https://leetcode.cn/problems/largest-element-in-an-array-after-merge-operations/
题解(贪心)
由于题目操作的前提是 nums[i] ≤ nums[i + 1],因此我们可以优先合并靠后的相邻序列,这样可以保证靠前的更多数能够被合并。如果中间出现 nums[i] 不小于 nums[i + 1] 的情况,说明遇到一个较大的数,它的权重大于后续数组的合并,我们则直接使用这个较大的数。
class Solution:
def maxArrayValue(self, nums: List[int]) -> int:
for i in range(len(nums) - 2, -1, -1):
if (nums[i] <= nums[i + 1]): nums[i] += nums[i + 1]
return nums[0]
class Solution {
fun maxArrayValue(nums: IntArray): Long {
val n = nums.size
var ret = Long.MIN_VALUE
for (i in n - 1 downTo 0) {
if (ret >= nums[i]) {
ret += nums[i]
} else {
ret = nums[i].toLong()
}
}
return ret
}
}
复杂度分析:
- 时间复杂度:$O(n)$ 线性遍历;
- 空间复杂度:$O(1)$ 仅使用常量级别空间。
T3. 长度递增组的最大数目(Hard)
https://leetcode.cn/problems/maximum-number-of-groups-with-increasing-length/
题解(排序)
输入数组相当于数字的出现频率,由于题目只关心构造组合的方案数而不关心数字的内容,数字本身是不重要的,因此我们可以先对频率数组排序,并从小到大枚举频率。
在构造的过程中,我们将当前遍历到的频率追加到已经拼接过的分组上(默认存在一个空分组),如果当前的频率不够或者超出,则将剩余元素放到候选容器中,严格证明见灵神的题解。
# 测试用例 [2, 2, 2]
0 => 0 1 => 0 1 2
1 1 2
0
# 测试用例 [1, 2, 5]
0 => 0 1 => 0 1 2
1 1 2
2
# 测试用例 [2, 1, 2] => 排序 [1, 2, 2]
0 => 0 1 => 0 1 2
1 1 2
# 测试用例 [1,1]
0
# 测试用例 [2, 100, 2, 2, 2] => 排序 [2, 2, 2, 2, 100]
0 => 0 1 => 0 1 2 => 跳过 => 0 1 2 3 => 剩余的 4 可以拼接,但不会产生增加分组数量
1 1 2 1 2 3
0 0 4
4
class Solution {
fun maxIncreasingGroups(usageLimits: List<Int>): Int {
Collections.sort(usageLimits)
var ret = 0
var left = 0L
for (x in usageLimits) {
left += x.toLong()
// 可以构造新分组
if (left >= ret + 1) {
ret ++
left -= ret
}
}
return ret
}
}
复杂度分析:
- 时间复杂度:$O(nlgn)$ 瓶颈在排序;
- 空间复杂度:$O(lgn)$ 瓶颈在排序。
T4. 树中可以形成回文的路径数(Hard)
https://leetcode.cn/problems/count-paths-that-can-form-a-palindrome-in-a-tree/
题解(状态压缩 + 前缀和 + 散列表)
1、回文判断: 首先,由于题目的回文串判断允许重排,因此回文串的 check 可以转换为字母的计数:
- 出现次数为奇数的字母最多只能出现 1 个;
- 出现次数为偶数的字母可以出现任意次。
2、奇偶性: 其次,由于题目的数组仅为小写字母,我们可以使用一个整型来压缩表示 26 个字母的出现次数状态,0 表示出现次数为偶数,1 表示出现次数为奇数。例如 0001 表示 ‘a’ 字母的出现次数为奇数,其他字母的出现次数为偶数(可能未出现)。
3、状态压缩: 基于以上 2 点,我们的目标是在树上找到两个点的路径 [u, v] 使得路径的状态 mask 满足以下其中 1 个条件:
- mask == 0:说明所有字母都出现偶数次;
- mask & (mask - 1) == 0:说明二进制位中 1 的出现次数为 1 次,即只有一个字母出现奇数次。
4、前缀和: 那么,如果如何求树上两点间的路径?这里有一个技巧,如果直接收集两个点之间的路径信息很难,我们可以先求从根节点到 u 的路径 root_u,以及从根节点到 v 的路径 root_v,再将两段路径异或就可以得到 u 到 v 的路径(如果两个节点的 LCA 不是根节点,那么重复的路径会被异或消除)。以题目示例 1 中节点 3 和节点 4 为例:path_3 = 0101,path_4 = 0110,而 path_3 xor path_4 = 0011,即 “ab”。
5、两数之和: 最后,我们的目标就变成:寻找从到根节点的路径异或值满足「分析 3」条件的路径,这可以用类似「两数之和」中的散列表的方法求解。
class Solution {
fun countPalindromePaths(parent: List<Int>, s: String): Long {
var ret = 0L
val cnt = HashMap<Int, Int>()
val memo = HashMap<Int, Int>()
// 两数之和模板
for (i in 0 until parent.size) {
val path = getPath(parent, s, memo, i)
// 1、两条路径异或值等于 0 的情况
ret += cnt.getOrDefault(path, 0)
for (j in 0 until 26) {
// 2、两条路径异或值中二进制位 1 只有 1 个的情况
ret += cnt.getOrDefault(path xor 1.shl(j), 0)
}
cnt[path] = cnt.getOrDefault(path, 0) + 1
}
return ret
}
private fun getPath(parent: List<Int>, s: String, memo: MutableMap<Int, Int>, i: Int): Int {
// 终止条件
if (i == 0) return 0
// 读备忘录
if (memo.containsKey(i)) return memo[i]!!
val path = getPath(parent, s, memo, parent[i]) xor (1.shl(s[i] - 'a')) // 增加一个计数等于奇偶性翻转
// 存备忘录
memo[i] = path
return path
}
}
复杂度分析:
- 时间复杂度:$O(Cn)$ getPath() DFS 的时间复杂度为 $O(n)$,枚举方案的时间复杂度为 $O(Cn)$,其中 $C$ 为字符集大小;
- 空间复杂度:$O(n)$ 记录每个节点到根节点的路径值空间。
推荐阅读
LeetCode 上分之旅系列往期回顾:
️ 永远相信美好的事情即将发生,欢迎加入小彭的 Android 交流社群~
LeetCode 周赛上分之旅 #35 两题坐牢,菜鸡现出原形的更多相关文章
- $\rm{NOIP}$前的模拟题整理·菜鸡互啄篇
嗯,打算整理一下我们机房菜鸡互啄中比较不错的题-- 大概情况就是每个人出三道题,然后互测这种感觉-- 至于某些Y姓基佬.Z姓基佬偷偷出原题--就不说了233 嗯,剩下的就先\(magpie\)着吧23 ...
- 刷爆 LeetCode 双周赛 100,单方面宣布第一题最难
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 100 场双周赛,你参加了吗?这场周赛整体没有 Hard ...
- LeetCode 周赛 342(2023/04/23)容斥原理、计数排序、滑动窗口、子数组 GCB
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 前天刚举办 2023 年力扣杯个人 SOLO 赛,昨天周赛就出了一场 Easy - Ea ...
- 刷爆 LeetCode 周赛 337,位掩码/回溯/同余/分桶/动态规划·打家劫舍/贪心
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 337 场周赛,你参加了吗?这场周赛第三题有点放水,如果 ...
- 【Leetcode周赛】从contest-41开始。(一般是10个contest写一篇文章)
Contest 41 ()(题号) Contest 42 ()(题号) Contest 43 ()(题号) Contest 44 (2018年12月6日,周四上午)(题号653—656) 链接:htt ...
- 【Leetcode周赛】从contest-81开始。(一般是10个contest写一篇文章)
Contest 81 (2018年11月8日,周四,凌晨) 链接:https://leetcode.com/contest/weekly-contest-81 比赛情况记录:结果:3/4, ranki ...
- 【Leetcode周赛】从contest-91开始。(一般是10个contest写一篇文章)
Contest 91 (2018年10月24日,周三) 链接:https://leetcode.com/contest/weekly-contest-91/ 模拟比赛情况记录:第一题柠檬摊的那题6分钟 ...
- 在vscode中配置LeetCode插件,从此愉快地刷题
大家好,今早在B站看到up主的vscode里藏了leetcode插件,这才知道原来还有这款神器.但是没想到在用的时候遇到了一些麻烦,花了一点时间才解决.所以写这篇文章除了给大家安利这个好用的插件之外, ...
- Leetcode 1577 数的平方等于两数乘积的方法数
Leetcode 1577 数的平方等于两数乘积的方法数 题目 给你两个整数数组 nums1 和 nums2 ,请你返回根据以下规则形成的三元组的数目(类型 1 和类型 2 ): 类型 1:三元组 ( ...
- CF上的3道小题(2)
CF上的3道小题(2) T1:CF630K Indivisibility 题意:给出一个数n,求1到n的数中不能被2到9中任意一个数整除的数. 分析:容斥一下,没了. 代码: #include < ...
随机推荐
- Analysis of Variance 方差分析
title: "Analysis of Variance" author: '01' date: "2022-11-23" output: html_docum ...
- 深度学习--魔法类nn.Module
深度学习--魔法类nn.Module 作用 pytorch 封装了一些基本的网络类,可以直接调用 好处: 可以直接调用现有的类 容器机制:self.net = nn.Sequential() 参数返回 ...
- Lambda表达式和链式编程
一.Lambda表达式 1. Lambda使用条件 Lambda表达式是 JDK8 的一个新特性,可以认为是对匿名内部类的一种简化,但不是所有的匿名内部类都可以简化为Lambda表达式. 只有函数式接 ...
- Python 列表的修改、添加和删除元素
列表修改.添加和删除元素 大多数创建的列表都是动态的,随程序的运行增删元素 修改列表元素 指定列表名和要修改的元素的索引,再指定要修改元素的新值 # 修改列表元素案例 motorcycles = [' ...
- Locust 界面简介(非使用级)
一.认识Locust 1.简介 Locust是一款易于使用的分布式负载测试工具,完全基于事件,即一个locust节点也可以在一个进程中支持数千并发用户,不使用回调,通过gevent使用轻量级过程(即在 ...
- 2021-05-18:Nim博弈。给定一个正数数组arr,先手和后手每次可以选择在一个位置拿走若干值, 值要大于0,但是要小于该处的剩余。谁最先拿空arr,谁赢。根据arr,返回谁赢 。
2021-05-18:Nim博弈.给定一个正数数组arr,先手和后手每次可以选择在一个位置拿走若干值, 值要大于0,但是要小于该处的剩余.谁最先拿空arr,谁赢.根据arr,返回谁赢 . 福大大 答案 ...
- wmi搜集一台计算机的硬件信息
作用: Python搜集一台计算机的硬件信息,借助模块:wmi,这个模块只支持window操作系统. 安装: pip install wmi 导入: import wmi 实例 c = wmi.WMI ...
- 一些JS过滤方法
一般过滤器我们都会卸载过滤filter文件内 本文这里就直接写正常methods格式的 //过滤空格 filterSpaces(data) { return data.replace(/\s+/g, ...
- [SWPUCTF 2021 新生赛]no_wakeup
[SWPUCTF 2021 新生赛]no_wakeup 考点 反序列化 一.题目 打开题目发现如下代码 <?php header("Content-type:text/html;cha ...
- 2013年蓝桥杯C/C++大学B组省赛真题(马虎的算式)
题目描述: 小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了. 有一次,老师出的题目是:36 x 495 = ? 他却给抄成了:396 x 45 = ? 但结果却很戏剧性,他的答案竟然是 ...