力扣周赛335
这场周赛共有四道题目,难度从简单到困难。下面是我使用Python3写的代码和解题思路。
题目一:递枕头
题目描述
给定一个长度为 n 的整数数组 nums ,下标从 0 开始。如果在下标 i 处 分割 数组,其中 0 <= i <= n - 2 ,使前 i + 1 个元素的乘积和剩余元素的乘积互质,则认为该分割 有效 。例如,如果 nums = [2, 3, 3] ,那么在下标 i = 0 处的分割有效,因为 2 和 9 互质,而在下标 i = 1 处的分割无效,因为 6 和 3 不互质。在下标 i = 2 处的分割也无效,因为 i == n - 1 。返回可以有效分割数组的最小下标 i ,如果不存在有效分割,则返回 -1 。当且仅当 gcd(val1, val2) == 1 成立时, val1 和 val2 这两个值才是互质的,其中 gcd(val1, val2) 表示 val1 和 val2 的最大公约数。
解题思路
这道题可以用一个双指针的方法来解决。我们用两个变量 i 和 j 来表示 s 中的一个子串 [i,j)(左闭右开),初始时 i=j=0。我们维护一个变量 ans 来记录最大字符数,初始为 0。
我们不断地向右移动 j 指针,直到遇到空格或者字符串末尾。此时,我们得到了 s 中的一个单词 word。我们检查 word 是否在 words 中,如果是,则说明可以替换这个单词,我们就将 ans 加上 word 的长度。如果不是,则说明不能替换这个单词,我们就将 ans 保持不变。
然后,我们将 i 指针移动到 j 指针的下一个位置(跳过空格),并重复上述过程,直到遍历完整个字符串 s。此时,ans 的值就是最终答案。
代码
class Solution:
def passThePillow(self, n: int, time: int) -> int:
# 初始化答案、双指针
ans = 0
i = 0
j = 0
# 将字符串数组转换为集合,方便查找
words = set(words)
# 遍历字符串
while j < len(s):
# 如果遇到空格或者字符串末尾,则得到一个单词
if s[j] == ' ' or j == len(s) - 1:
# 如果是字符串末尾,则需要加上最后一个字符
if j == len(s) - 1:
j += 1
# 获取当前单词
word = s[i:j]
# 如果当前单词在字符串数组中,则更新答案
if word in words:
ans += len(word)
# 将i指针移动到j指针的下一个位置(跳过空格)
i = j + 1
# 否则继续向右移动j指针
j += 1
# 返回答案
return ans
复杂度分析
- 时间复杂度:O(n+m),其中 n 是字符串 s 的长度,m 是字符串数组 words 的长度。我们需要遍历一次字符串 s,并且将字符串数组转换为集合需要 O(m) 的时间。
- 空间复杂度:O(m),我们需要额外使用一个集合来存储字符串数组中的单词。
题目二:二叉树中的第 K 大层和
题目描述
给你一棵二叉树的根节点 root 和一个正整数 k 。树中的 层和 是指 同一层 上节点值的总和。返回树中第 k 大的层和(不一定不同)。如果树少于 k 层,则返回 -1 。注意 ,如果两个节点与根节点的距离相同,则认为它们在同一层。
解题思路
这道题可以用广度优先搜索(BFS)的方法来解决。我们用一个队列来存储每一层的节点,然后用一个变量来记录每一层的和。我们还需要用一个小顶堆来维护最大的 k 个层和,如果堆的大小小于 k,则直接将当前层和加入堆中;如果堆的大小等于 k,则比较堆顶元素和当前层和,如果当前层和更大,则将堆顶元素弹出,然后将当前层和加入堆中。这样,当遍历完整棵树后,堆顶元素就是第 k 大的层和,如果堆的大小小于 k,则说明树的层数不足 k,返回 -1。
代码
import heapq
class Solution:
def kthLargestLevelSum(self, root: TreeNode, k: int) -> int:
# 初始化答案、队列、堆
ans = 0
queue = []
heap = []
# 将根节点入队
queue.append(root)
# 广度优先搜索
while queue:
# 记录当前层的和
level_sum = 0
# 遍历当前层的所有节点
for _ in range(len(queue)):
# 取出队首节点
node = queue.pop(0)
# 累加节点值
level_sum += node.val
# 将左右子节点入队(如果存在)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
# 维护最大的k个层和
if len(heap) < k:
# 堆的大小小于k,直接加入
heapq.heappush(heap, level_sum)
elif heap[0] < level_sum:
# 堆的大小等于k,比较堆顶元素和当前层和,如果当前层和更大,则替换堆顶元素
heapq.heapreplace(heap, level_sum)
# 返回答案
return heap[0] if len(heap) == k else -1
复杂度分析
- 时间复杂度:O(nlogk),其中 n 是节点数。二叉树每个节点最多入队一次,二叉树最大有 n 层,小顶堆维护 k 个数的时间复杂度为 O(logk);
- 空间复杂度:O(n),我们需要额外使用一个队列来存储每一层的节点,空间复杂度为 O(n);还需要额外使用一个小顶堆来维护最大的 k 个层和,空间复杂度为 O(k)。
题目四:获得分数的方法数
题目描述
给你一个长度为 n 的整数数组 nums ,下标从 0 开始。如果在下标 i 处 分割 数组,其中 0 <= i <= n - 2 ,使前 i + 1 个元素的乘积和剩余元素的乘积互质,则认为该分割 有效 。例如,如果 nums = [2, 3, 3] ,那么在下标 i = 0 处的分割有效,因为 2 和 9 互质,而在下标 i = 1 处的分割无效,因为 6 和 3 不互质。在下标 i = 2 处的分割也无效,因为 i == n - 1 。返回可以有效分割数组的最小下标 i ,如果不存在有效分割,则返回 -1 。当且仅当 gcd(val1, val2) == 1 成立时, val1 和 val2 这两个值才是互质的,其中 gcd(val1, val2) 表示 val1 和 val2 的最大公约数。
给你一个长度为 n 的整数数组 nums ,下标从 0 开始。如果在下标 i 处 分割 数组,其中 0 <= i <= n - 2 ,使前 i + 1 个元素的乘积和剩余元素的乘积互质,则认为该分割 有效 。例如,如果 nums = [2, 3, 3] ,那么在下标 i = 0 处的分割有效,因为 2 和 9 互质,而在下标 i = 1 处的分割无效,因为 6 和 3 不互质。在下标 i = 2 处的分割也无效,因为 i == n - 1 。返回可以有效分割数组的最小下标 i ,如果不存在有效分割,则返回 -1 。当且仅当 gcd(val1, val2) == 1 成立时, val1 和 val2 这两个值才是互质的,其中 gcd(val1, val2) 表示 val1 和 val2 的最大公约数。
解题思路
这道题可以用动态规划的方法来解决。我们用一个二维数组 dp[i][j] 来表示前 i + 1 题中得到 j 分的方法数。初始时,dp[0][0] = dp[0][nums[0]] = dp[0][total - nums[0]] = dp[0][total] = 1,表示第一题只有四种可能的得分:0、nums[0]、total - nums[0]、total。其他位置都初始化为零。
然后我们从第二题开始遍历,对于每一题,我们有两种选择:正确或错误。如果选择正确,则当前得分加上题目分数;如果选择错误,则当前得分减去题目分数。我们用一个临时数组 temp 来存储当前题目对应的得分方法数,然后更新 dp 数组。
最后我们返回 dp[n - 1][target] 的值,即前 n 题中得到 target 分的方法数。
代码
class Solution:
def scoreOfStudents(self, s: str, answers: List[int]) -> int:
# 计算表达式正确结果
correct = eval(s)
# 计算表达式所有可能结果
possible = set()
# 将表达式转换为后缀表达式
postfix = []
stack = []
for c in s:
if c.isdigit():
postfix.append(int(c))
elif c == '+':
while stack and stack[-1] == '*':
postfix.append(stack.pop())
stack.append(c)
elif c == '*':
stack.append(c)
while stack:
postfix.append(stack.pop())
# 计算后缀表达式所有可能结果
def calc(postfix):
stack = []
for x in postfix:
if isinstance(x, int):
stack.append(x)
else:
b = stack.pop()
a = stack.pop()
if x == '+':
stack.append(a + b)
else:
stack.append(a * b)
return stack[-1]
# 枚举所有可能的括号位置
def dfs(start, end):
if start == end:
possible.add(postfix[start])
return
for i in range(start, end, 2):
dfs(start, i)
dfs(i + 2, end)
left = list(possible)
right = list(possible)
for x in left:
for y in right:
postfix[i + 1] = '+'
possible.add(calc(postfix[start:end + 1]))
postfix[i + 1] = '*'
possible.add(calc(postfix[start:end + 1]))
dfs(0, len(postfix) - 1)
# 计算答案
ans = 0
for answer in answers:
if answer == correct:
ans += 5
elif answer in possible:
ans += 2
return ans
复杂度分析
- 时间复杂度:O(n^3),其中 n 是表达式的长度。我们需要遍历所有可能的括号位置,每次计算后缀表达式的结果需要 O(n) 的时间,最后遍历答案数组需要 O(n) 的时间。
- 空间复杂度:O(n^2),我们需要额外使用一个集合来存储所有可能的结果,空间复杂度为 O(n^2);还需要额外使用一个栈来转换和计算后缀表达式,空间复杂度为 O(n)。