LeetCode 第 46 场双周赛题解
t1:5668. 最长的美好子字符串(简单)
当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是 美好 字符串。
比方说,"abABB" 是美好字符串,因为 'A' 和 'a' 同时出现了,且 'B' 和 'b' 也同时出现了。
然而,"abA" 不是美好字符串因为 'b' 出现了,而 'B' 没有出现。
给你一个字符串 s ,请你返回 s 最长的 美好子字符串 。
如果有多个答案,请你返回 最早 出现的一个。如果不存在美好子字符串,请你返回一个空字符串。
示例 1:
示例 2:
示例 3:
示例 4:
提示:
1 <= s.length <= 100
s 只包含大写和小写英文字母。
朴素解法
由于数据范围只有 100,因此怎么做都可以。
对于这种数据范围的题,大家尽量选择 简单的、出错率低 的做法。
一个直观的做法是,枚举每个子串的「起点」和「终点」,检查子串中的每个字符,是否在子串同时包含小写字母和大写字母。
时间复杂度:$O(n^3)$
空间复杂度:$O(1)$
t2:5669. 通过连接另一个数组的子数组得到一个数组(中等)
给你一个长度为 n 的二维整数数组 groups ,同时给你一个整数数组 nums 。
你是否可以从 nums 中选出 n 个 不相交 的子数组,使得第 i 个子数组与 groups[i] (下标从 0 开始)完全相同,且如果 i > 0 ,那么第 (i-1) 个子数组在 nums 中出现的位置在第 i 个子数组前面。(也就是说,这些子数组在 nums 中出现的顺序需要与 groups 顺序相同)
如果你可以找出这样的 n 个子数组,请你返回 true ,否则返回 false 。
如果不存在下标为 k 的元素 nums[k] 属于不止一个子数组,就称这些子数组是 不相交 的。子数组指的是原数组中连续元素组成的一个序列。
示例 1:
示例 2:
示例 3:
提示:
groups.length == n
1 <= n <= $10^3$
1 <= groups[i].length, sum(groups[i].length) <= $10^3$
1 <= nums.length <= $10^3$
$-10^7$ <= groups[i][j], nums[k] <= $10^7$
朴素解法
本质上是道模拟题。
使用 i
指针代表 nums
当前枚举到的位置;j
代表 groups
中枚举到哪个数组。
cnt
代表匹配的数组个数。
当
i
开头的连续一段和groups[j]
匹配:j
指针右移一位(代表匹配下一个数组),i
指针右移groups[j].length
长度。当
i
开头的连续一段和groups[j]
不匹配:i
右移一位。
代码:
时间复杂度:$O(n * m)$
空间复杂度:$O(1)$
t3:5671. 地图中的最高点(中等)
给你一个大小为 m x n 的整数矩阵 isWater,它代表了一个由陆地和*水域*单元格组成的地图。
如果
isWater[i][j] == 0
,格子 (i, j) 是一个陆地格子。如果
isWater[i][j] == 1
,格子 (i, j) 是一个水域格子。
你需要按照如下规则给每个单元格安排高度:
每个格子的高度都必须是非负的。
如果一个格子是是 水域 ,那么它的高度必须为 0 。
任意相邻的格子高度差 至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着,就称它们为相邻的格子。(也就是说它们有一条公共边)
找到一种安排高度的方案,使得矩阵中的最高高度值 最大 。
请你返回一个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法,请返回 任意一个 。
示例 1:
示例 2:
提示:
m == isWater.length
n == isWater[i].length
1 <= m, n <= 1000
isWater[i][j] 要么是 0 ,要么是 1 。
至少有 1 个水域格子。
多源 BFS 解法(Flood Fill)
假设只有一个「水域」的话,我们会如何求解?
显然,「水域」所在的位置是 0
,而从「水域」出发一步可达的位置是 1
,1
位置一步可达的位置 2
...
这是一个 BFS 的过程。
当只有一个水域时,是个「单源 BFS」问题。
而本题有多个水域,相应的我们可以使用「多源 BFS」解决。
「多源 BFS」和「单源 BFS」本质上其实并无区别,都遵循以下步骤:
1. 将「起点」进行入队
2. 弹出队列中的元素,将从「弹出元素」出发一步可达(并符合条件)的节点,加入队列
3. 重复步骤 2,直到队列为空(代表没有节点需要更新)
值得注意的是,在我们熟悉的「单源 BFS」中,通常我们会建一个 boolean 数组来记录哪些点已经入过队列。
但其实我们本质上不是为了记录哪些点入过队列,而是为了记录哪些节点不再需要被更新。
对于「单源 BFS」而言,每个节点只会被更新一次,不会遇到需要被多次更新的情况。
因此每个节点入队一次之后我们就使用 boolean 数组来标记「不再需要被更新」。
而对于本题(多源 BFS)而言,一个节点是需要被多次更新的。
为了方便理解,我们可以将整个过程拆开来看,以上面的 「示例 2」 为例子:
先忽略水域 2 ,使用水域 1 作为起点更新整个区域(这时候得到的答案是满足水域 1 而不满足水域 2 的)
然后再以水域 2 为起点更新区域,对于那些高度与水域 2 冲突的值,使用满足条件的值对其进行覆盖(用更小的值去更新冲突的区域)
所有的水域作为起点更新完之后,最终得到的就是满足所有水域条件的答案
可见,这时候我们不能与「单源 BFS」一样,以「是否入过队列」作为「是否需要更新」的标准,一个节点是否需要被更新应该「取决于当前高度是否过高(会造成冲突)」。
所以完整的流程应该是:
将所有的「起点」,也就是水域进行入队
弹出队列中的元素,判断「弹出元素」的相邻元素的高度是否需要被更新,如果相邻元素需要被更新,则将相邻元素进行入队
重复步骤 2,直到队列为空(代表没有节点需要更新)
代码:
时间复杂度:节点的总数量为
n
,水域节点的数量为m
。每个节点入队的次数与水域数量有关。复杂度为 $O(n * m)$空间复杂度:$O(n)$
t4:5670. 互质树(困难)
给你一个 n 个节点的树(也就是一个无环连通无向图),节点编号从 0 到 n - 1 ,且恰好有 n - 1 条边,每个节点有一个值。树的 根节点 为 0 号点。
给你一个整数数组 nums 和一个二维数组 edges 来表示这棵树。nums[i] 表示第 i 个点的值,edges[j] = [uj, vj] 表示节点 uj 和节点 vj 在树中有一条边。
当 gcd(x, y) == 1 ,我们称两个数 x 和 y 是 互质的 ,其中 gcd(x, y) 是 x 和 y 的 最大公约数 。
从节点 i 到 根 最短路径上的点都是节点 i 的祖先节点。一个节点 不是 它自己的祖先节点。
请你返回一个大小为 n 的数组 ans ,其中 ans[i]是离节点 i 最近的祖先节点且满足 nums[i] 和 nums[ans[i]] 是 互质的 ,如果不存在这样的祖先节点,ans[i] 为 -1 。
示例 1:
示例 2:
提示:
nums.length == n
1 <= nums[i] <= 50
1 <= n <= $10^5$
edges.length == n - 1
edges[j].length == 2
0 <= uj, vj < n
uj != vj
基本思路
题目描述很长,但其实就是说每个节点从下往上找,找到最近的「与其互质」的节点。
数据范围是 $10^5$,如果每个节点都直接往上找最近「互质」祖宗节点的话,当树为线性时,复杂度是 $O(n^2)$ ,会超时。
因此我们要利用 nums[i] 范围只有 50 的特性。
我们可以先预处理除 [1, 50] 范围内的每个数,求出他们互质的数有哪些,存到一个字典里。
那么对于某个节点而言,假设节点的值为 x
,所在层数为 y
。
那么问题转化为求与 x
互质的数有哪些,最近的在哪一层。
用 dep[x]
表示距离值为 x
的节点最近的层是多少;pos[x]
代表具体的节点编号。
DFS 解法
代码:
时间复杂度:对于每个节点而言,会检查与其数值互质的数有哪些,在哪层。最坏情况下会检查 50 个互质数(当前数值为 1)。复杂度为 $O(n)$
空间复杂度:$O(n)$
最后
这是 2021/02/21 的 LeetCode 第 46 场双周赛的比赛题解。
除了前两道模拟题以外,这次周赛与「搜索」相关的题比较多。
t3 是一道「多源 BFS」裸题,t4 是一道脑筋急转弯的 DFS 搜索题。
版权声明: 本文为 InfoQ 作者【宫水三叶的刷题日记】的原创文章。
原文链接:【http://xie.infoq.cn/article/e240e26dac5ded4b3a726f764】。文章转载请联系作者。
评论