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

双周赛 108 概览

T1. 最长交替子序列(Easy)

  • 标签:模拟、同向双指针

T2. 重新放置石块(Medium)

  • 标签:模拟、散列表

T3. 将字符串分割为最少的美丽子字符串(Medium)

  • 标签:记忆化递归、动态规划

T4. 黑格子的数目(Medium)

  • 标签:枚举、贡献


T1. 最长交替子序列(Easy)

https://leetcode.cn/problems/longest-alternating-subarray/

题解一(模拟)

这道题与上周周赛 T1 还是比较相似的。

使用两层循环,枚举从每个元素 nums[i] 为起点开始的最长交替子序列长度。

class Solution {
fun alternatingSubarray(nums: IntArray): Int {
var ret = -1
for (i in 0 until nums.size) {
var target = 1
for (j in i + 1 until nums.size) {
if (nums[j] - nums[j - 1] != target) break
ret = Math.max(ret, j - i + 1)
target *= -1
}
}
return ret
}
}

复杂度分析:

  • 时间复杂度:$O(n^2)$ 其中 n 为 nums 数组的长度;
  • 空间复杂度:仅使用常量级别空间。

题解二(同向双指针)

这个解法基于 KMP 思想。

在题解一中,我们会重复计算同一段交替子序列的,我们可以使用一次遍历,再交替子序列终止时避免重复回退到该子序列内部。需要注意的是,由于不同的交替子序列可能存在 1 位重叠,所以要把 i 指针指向 j 指针,而不是指向 j 指针的下一位,才能保证没有缺失。例如 [3,4,3,4,5,4,5] 数组,第一组交替子数组为 [3,4,3,4] 和第二组交替子数组为 [4,5,4,5] 这两组有重叠部分。

class Solution {
fun alternatingSubarray(nums: IntArray): Int {
val n = nums.size
var ret = -1
var i = 0
while (i < n - 1) {
// 寻找起点
while (i < n - 1 && nums[i + 1] - nums[i] != 1) {
i++
}
var target = 1
var j = i
while (j < n - 1 && nums[j + 1] - nums[j] == target) {
ret = Math.max(ret, ++j - i + 1)
target *= -1
}
i = j
}
return ret
}
}

复杂度分析:

  • 时间复杂度:$O(n)$ 线性遍历
  • 空间复杂度:$O(1)$ 仅使用常量级别空间。

T2. 重新放置石块(Medium)

https://leetcode.cn/problems/relocate-marbles/

题解(模拟 + 散列表)

在每部操作中,我们会将位置 moveFrom[i] 上所有的石头移动到 moveTo[i] 上,「所有」的含义意味着石头的数量是无关紧要的,我们可以使用散列表维护剩余的石头,最后对剩余石头排序。

class Solution {
fun relocateMarbles(nums: IntArray, moveFrom: IntArray, moveTo: IntArray): List<Int> {
if (moveFrom.size != moveTo.size) return Collections.emptyList()
val set = nums.toHashSet()
for (i in moveFrom.indices) {
set.remove(moveFrom[i])
set.add(moveTo[i])
}
return set.toMutableList().sorted()
}
}

复杂度分析:

  • 时间复杂度:$O(nlgn)$ 瓶颈在排序上;
  • 空间复杂度:$O(n)$ 散列表空间。

T3. 将字符串分割为最少的美丽子字符串(Medium)

https://leetcode.cn/problems/partition-string-into-minimum-beautiful-substrings/

题解一(记忆化递归)

比较直观的子集问题,我们枚举所有分割点(可以构造 5 的幂)的位置并记录最短结果。由于题目的数据范围比较小,我们可以预处理出数据范围内所有 5 的幂。

  • 定义 backTrack(i) 表示从 [i] 为起点的最少美丽字符串个数,枚举以 [i] 为起点的所有可行方案,从中得出最优解。
class Solution {

    companion object {
// 预处理
private val U = 15
private val INF = Integer.MAX_VALUE
private val set = HashSet<Int>()
init {
var x = 1
while (x.toString(2).length <= U) {
set.add(x)
x *= 5
}
}
} fun minimumBeautifulSubstrings(s: String): Int {
return backTrack(s, HashMap<Int,Int>(), 0)
} private fun backTrack(s: String, memo: MutableMap<Int, Int>, i: Int): Int {
// 终止条件
if (i == s.length) return 0
// 剪枝(不允许前导零)
if (s[i] == '0') return -1
// 读备忘录
if (memo.contains(i)) return memo[i]!!
// 枚举
var x = 0
var ret = INF
for (j in i until s.length) {
x = x.shl(1) + (s[j] - '0')
if (set.contains(x)) {
// 递归
val childRet = backTrack(s, memo, j + 1)
if (-1 != childRet) ret = Math.min(ret, childRet)
}
}
val finalRet = if (INF == ret) -1 else ret + 1
memo[i] = finalRet
return finalRet
}
}

复杂度分析:

  • 时间复杂度:$O(n^2)$ 一共 n 个分割点,每个分割点有「选和不选」两种方案,看起来总共有 $2^n$ 种子状态,其实并没有。我们的 backTrack(i) 的定义是以 [i] 为起点可以构造的最少美丽字符串数,因此总共只有 n 种状态,而每种状态需要检查 $O(n)$ 种子状态,因此整体时间复杂度是 $O(n^2)$;
  • 空间复杂度:$O(n)$ 备忘录空间。

题解二(动态规划)

可以把记忆化递归翻译为动态规划的版本:

class Solution {

    companion object {
// 预处理
private val U = 15
private val INF = Integer.MAX_VALUE
private val set = HashSet<Int>()
init {
var x = 1
while (x.toString(2).length <= U) {
set.add(x)
x *= 5
}
}
} fun minimumBeautifulSubstrings(s: String): Int {
val INF = 0x3F3F3F3F // 便于判断
val n = s.length
val dp = IntArray(n + 1) { INF }
dp[n] = 0
// 倒序遍历(先求小问题)
for (i in n - 1 downTo 0) {
// 不允许前导零
if (s[i] == '0') continue
// 枚举
var x = 0
for (j in i until n) {
x = x.shl(1) + (s[j] - '0')
if (set.contains(x)) dp[i] = Math.min(dp[i], dp[j + 1] + 1)
}
}
return if (dp[0] != INF) dp[0] else -1
}
}

复杂度分析:

  • 时间复杂度:$O(n^2)$ 同上;
  • 空间复杂度:$O(n)$ DP 数组空间。

T4. 黑格子的数目(Medium)

https://leetcode.cn/problems/number-of-black-blocks/

题解(枚举黑格 + 贡献度)

直接枚举所有块的时间复杂度是 O(nm) 会超时,我们发现真正影响结果的是黑格格子,但是暴力枚举块的方法会枚举到那些完全是白色的块。

因此,我们将枚举维度从所有块调整到黑色格子附近的块,对于每一个黑色格子 [x, y] 最多仅会对 4 个块产生影响(贡献)。所以我们的算法是:枚举所有黑色格子,并记录黑色格子可以产生贡献的块,最后统计出所有可以被影响到的块以及的贡献度,这可以用散列表来记录。

剩下一个问题是怎么表示一个唯一的块,我们可以规定块中 4 个点中的其中一个点作为块的代表元(以右下角的点为例),然后将该点的行和列压缩到一个 Long 变量中来唯一标识不同的块。


class Solution {
fun countBlackBlocks(m: Int, n: Int, coordinates: Array<IntArray>): LongArray {
val U = 100000
val map = HashMap<Long, Int>()
// 以右下角为代表元的块
val blocks = arrayOf(intArrayOf(0,0), intArrayOf(0, 1), intArrayOf(1,1), intArrayOf(1,0))
for (e in coordinates) {
// 枚举 4 个块
for (block in blocks) {
val x = e[0] + block[0]
val y = e[1] + block[1]
// 检查块有效性
if (x >= 1 && x < m && y >= 1 && y < n) {
// 记录贡献度
val key = 1L * x * U + y
map[key] = map.getOrDefault(key, 0) + 1
}
}
}
val ret = LongArray(5)
for ((_, cnt) in map) {
ret[cnt] ++
}
ret[0] = 1L * (n - 1) * (m - 1) - map.size
return ret
}
}

复杂度分析:

  • 时间复杂度:$O(m)$ 其中 m 为黑格格子数
  • 空间复杂度:$O(m)$ 其中 m 为黑格格子数

往期回顾

LeetCode 周赛(2023/07/08)渐入佳境的更多相关文章

  1. LeetCode 周赛 342(2023/04/23)容斥原理、计数排序、滑动窗口、子数组 GCB

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 前天刚举办 2023 年力扣杯个人 SOLO 赛,昨天周赛就出了一场 Easy - Ea ...

  2. http://www.blogjava.net/xylz/archive/2010/07/08/325587.html

    http://www.blogjava.net/xylz/archive/2010/07/08/325587.html

  3. 刷爆 LeetCode 周赛 337,位掩码/回溯/同余/分桶/动态规划·打家劫舍/贪心

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 337 场周赛,你参加了吗?这场周赛第三题有点放水,如果 ...

  4. 【Leetcode周赛】从contest-111开始。(一般是10个contest写一篇文章)

    Contest 111 (题号941-944)(2019年1月19日,补充题解,主要是943题) 链接:https://leetcode.com/contest/weekly-contest-111 ...

  5. LeetCode 周赛 332,在套路里摸爬滚打~

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,今天是 3T 选手小彭. 上周是 LeetCode 第 332 场周赛,你参加了吗?算法解题思维需要 ...

  6. LeetCode 周赛 333,你管这叫 Medium 难度?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周是 LeetCode 第 333 场周赛,你参加了吗?这场周赛质量很高,但难度标得不 ...

  7. LeetCode 周赛 334,在算法的世界里反复横跳

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 今天是 LeetCode 第 334 场周赛,你参加了吗?这场周赛考察范围比较基础,整体 ...

  8. LeetCode 周赛 336,多少人直接 CV?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 今天早上是 LeetCode 第 336 场周赛,你参加了吗?这场周赛整体质量比较高,但 ...

  9. LeetCode 周赛 338,贪心 / 埃氏筛 / 欧氏线性筛 / 前缀和 / 二分查找 / 拓扑排序

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 338 场周赛,你参加了吗?这场周赛覆盖的知识点很多,第 ...

  10. 刷爆 LeetCode 周赛 339,贪心 / 排序 / 拓扑排序 / 平衡二叉树

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 339 场周赛,你参加了吗?这场周赛覆盖的知识点比较少, ...

随机推荐

  1. LeeCode 316周赛复盘

    T1:判断两个事件是否存在冲突 思路:判断两个区间是否有交集 public boolean haveConflict(String[] event1, String[] event2) { // 比较 ...

  2. devops工具链基建建设评价标准

    之所以写这篇是因为有朋友私下让我完善下基建建设的标准和四个阶梯划分,然后让我一定要把腾讯和百度加到基建建设的排名中(看热闹不嫌事大). 基建infra建设四个考察维度 1)工具链完整性:该有的工具是否 ...

  3. 帝国cms将没有搜索到结果的关键字存入到数据库的方法

    在帝国cms网站前台搜索一个关键字,如果在网站中查询到了,这个关键字会被记录入搜索关键字表中,但是如果在网站中没有搜索到,就不会记录入搜索关键字表中,那怎么把没有搜索结果的关键字才能记录到数据库中,方 ...

  4. 去中心化金融-Lec3

    区块链 (什么是区块链?)区块链提供了一种无需可信的参与方,能够让多个参与方实现合作的方式(若存在可信的第三方,则不需要区块链:但是金融系统中往往没有可信的第三方) 终端用户工具:user inter ...

  5. Linux安装Net7SDK运行Net项目

    Linux安装Net7SDK运行Net项目 安装Net7 SDK 1.安装sdk依赖环境 wget https://packages.microsoft.com/config/ubuntu/20.04 ...

  6. 在Linux上安装redis7

    1.检测虚拟机环境 1.1 bit检测命令:getconf LONG_BIT(建议使用64bit做开发) 1.2 gcc环境检测:gcc -v 如果不具备gcc环境,则使用yum -y install ...

  7. .gitignore 文件语法介绍

    .gitignore 文件的作用 A gitignore file specifies intentionally untracked files that Git should ignore. Fi ...

  8. Linux运维实战项⽬进阶

    项⽬描述 项⽬需求 近年来为适应业务发展的需求,世界500强XX企业准备进⾏⼤规模的电⼦商务建设, 同时,希望能通过Linux平台,利⽤开源技术,来实现⼤型互联⽹电⼦商务⽹站架构建设和业务⽀撑,现要求 ...

  9. 2023-03-23:音视频解混合(demuxer)为PCM和YUV420P,用go语言编写。

    2023-03-23:音视频解混合(demuxer)为PCM和YUV420P,用go语言编写. 答案2023-03-23: 大体步骤如下: 1.打开媒体文件,并获取音频和视频流. 2.对于每个流,找到 ...

  10. 2022-06-30:以下golang代码输出什么?A:0;B:2;C:运行错误。 package main import “fmt“ func main() { ints := make

    2022-06-30:以下golang代码输出什么?A:0:B:2:C:运行错误. package main import "fmt" func main() { ints := ...