力扣周赛335

48 阅读2分钟

力扣周赛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)。