leetcode周赛-229

文章目录

  • 5685. 交替合并字符串
    • 题目
    • 解题思路
    • 代码
  • 5686. 移动所有球到每个盒子所需的最小操作数
    • 题目
    • 解题思路
    • 代码
  • 5687. 执行乘法运算的最大分数
    • 题目
    • 解题思路
    • 代码
  • 5688. 由子序列构造的最长回文串的长度
    • 题目
    • 解题思路
    • 代码

5685. 交替合并字符串

AC

题目

给你两个字符串 word1 和 word2 。请你从 word1 开始,通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。

返回 合并后的字符串 。

示例 1:

输入:word1 = "abc", word2 = "pqr"
输出:"apbqcr"
解释:字符串合并情况如下所示:
word1:  a   b   c
word2:    p   q   r
合并后:  a p b q c r

示例 2:

输入:word1 = "ab", word2 = "pqrs"
输出:"apbqrs"
解释:注意,word2 比 word1 长,"rs" 需要追加到合并后字符串的末尾。
word1:  a   b 
word2:    p   q   r   s
合并后:  a p b q   r   s

示例 3:

输入:word1 = "abcd", word2 = "pq"
输出:"apbqcd"
解释:注意,word1 比 word2 长,"cd" 需要追加到合并后字符串的末尾。
word1:  a   b   c   d
word2:    p   q 
合并后:  a p b q c   d

提示:

1 <= word1.length, word2.length <= 100
word1 和 word2 由小写英文字母组成

解题思路

没什么好说的了,类似合并2个有序数组,时间复杂度 o ( m a x ( w o r d 1. l e n g h t , w o r d 2. l e n g t h ) ) o(max(word1.lenght,word2.length)) o(max(word1.lenght,word2.length))

代码

class Solution:
    def mergeAlternately(self, word1: str, word2: str) -> str:
        ret_str = ''
        index1, index2 = 0, 0
        while index1 < len(word1) and index2 < len(word2):
            ret_str += word1[index1]
            index1 += 1
            ret_str += word2[index2]
            index2 += 1
        if index1 < len(word1):
            ret_str += word1[index1:]
        if index2 < len(word2):
            ret_str += word2[index2:]
        return ret_str

5686. 移动所有球到每个盒子所需的最小操作数

AC

题目

有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes ,其中 boxes[i] 的值为 ‘0’ 表示第 i 个盒子是 空 的,而 boxes[i] 的值为 ‘1’ 表示盒子里有 一个 小球。

在一步操作中,你可以将 一个 小球从某个盒子移动到一个与之相邻的盒子中。第 i 个盒子和第 j 个盒子相邻需满足 abs(i - j) == 1 。注意,操作执行后,某些盒子中可能会存在不止一个小球。

返回一个长度为 n 的数组 answer ,其中 answer[i] 是将所有小球移动到第 i 个盒子所需的 最小 操作数。

每个 answer[i] 都需要根据盒子的 初始状态 进行计算。

示例 1:

输入:boxes = "110"
输出:[1,1,3]
解释:每个盒子对应的最小操作数如下:
1) 第 1 个盒子:将一个小球从第 2 个盒子移动到第 1 个盒子,需要 1 步操作。
2) 第 2 个盒子:将一个小球从第 1 个盒子移动到第 2 个盒子,需要 1 步操作。
3) 第 3 个盒子:将一个小球从第 1 个盒子移动到第 3 个盒子,需要 2 步操作。将一个小球从第 2 个盒子移动到第 3 个盒子,需要 1 步操作。共计 3 步操作。

示例 2:

输入:boxes = "001011"
输出:[11,8,5,4,3,4]

提示:

n == boxes.length
1 <= n <= 2000
boxes[i] 为 '0' 或 '1'

解题思路

蛮简单的题目,首先记录有小球的盒子位置,然后从前向后遍历,对每个位置计算把其他小球挪过来的步骤,具体来说就是小球的位置和当前位置的差。时间复杂度 o ( n ) o(n) o(n)

代码

class Solution:
    def minOperations(self, boxes: str) -> List[int]:
        ball_indexs = [index for index, item in enumerate(boxes) if item == '1']
        ans = [0] * len(boxes)
        for index in range(len(ans)):
            ans[index] = sum([abs(ball_i - index) for ball_i in ball_indexs])
        return ans

5687. 执行乘法运算的最大分数

TLE
这道题赛中写得差不多了,一直TLE,赛后修改完才通过……

题目

给你两个长度分别 n 和 m 的整数数组 nums 和 multipliers ,其中 n >= m ,数组下标 从 1 开始 计数。

初始时,你的分数为 0 。你需要执行恰好 m 步操作。在第 i 步操作(从 1 开始 计数)中,需要:

选择数组 nums 开头处或者末尾处 的整数 x 。
你获得 multipliers[i] * x 分,并累加到你的分数中。
将 x 从数组 nums 中移除。
在执行 m 步操作后,返回 最大 分数。

示例 1:

输入:nums = [1,2,3], multipliers = [3,2,1]
输出:14
解释:一种最优解决方案如下:
- 选择末尾处的整数 3 ,[1,2,3] ,得 3 * 3 = 9 分,累加到分数中。
- 选择末尾处的整数 2 ,[1,2] ,得 2 * 2 = 4 分,累加到分数中。
- 选择末尾处的整数 1 ,[1] ,得 1 * 1 = 1 分,累加到分数中。
总分数为 9 + 4 + 1 = 14 。

示例 2:

输入:nums = [-5,-3,-3,-2,7,1], multipliers = [-10,-5,3,4,6]
输出:102
解释:一种最优解决方案如下:
- 选择开头处的整数 -5 ,[-5,-3,-3,-2,7,1] ,得 -5 * -10 = 50 分,累加到分数中。
- 选择开头处的整数 -3 ,[-3,-3,-2,7,1] ,得 -3 * -5 = 15 分,累加到分数中。
- 选择开头处的整数 -3 ,[-3,-2,7,1] ,得 -3 * 3 = -9 分,累加到分数中。
- 选择末尾处的整数 1 ,[-2,7,1] ,得 1 * 4 = 4 分,累加到分数中。
- 选择末尾处的整数 7 ,[-2,7] ,得 7 * 6 = 42 分,累加到分数中。
总分数为 50 + 15 - 9 + 4 + 42 = 102 。

提示:

n == nums.length
m == multipliers.length
1 <= m <= 10^3
m <= n <= 10^5
-1000 <= nums[i], multipliers[i] <= 1000

解题思路

v1
最直观的思路,记f(N, M)为输出结果的函数,则可以递归地:
f(N, M) = max(N[0] * M[0] + f(N[1:], M[1:]), N[-1] * M[0] + f(N[:-1], M[1:])
这样的话可以直接写出递归版代码1,但是会超时。

v2
考虑用memo保存中间状态,也即以(N, M)作为key保存一个字典,然后发现这样会超内存。

v3
发现由于nums[i][-1000,1000]之间,考虑用位存储。但是这样只能把每个数字占的空间缩小,仍然无法解决超出内存的问题

v4
发现可以用dp来解决。用dp[i][j]表示nums[i:j]multipliers相乘的最大结果,则:
d p [ i ] [ j ] = m a x { d p [ i + 1 ] [ j ] + n u m s [ i ] ∗ m u l t i p l i e r s [ i + n u m s . l e n − j − 1 ] d p [ i ] [ j − 1 ] + n u m s [ j ] ∗ m u l t i p l i e r s [ i + n u m s . l e n − j − 1 ] dp[i][j] = max \begin{cases} dp[i+1][j] + nums[i] * multipliers[i + nums.len - j - 1] \\ dp[i][j - 1] + nums[j] * multipliers[i + nums.len - j - 1] \end{cases} dp[i][j]=max{ dp[i+1][j]+nums[i]multipliers[i+nums.lenj1]dp[i][j1]+nums[j]multipliers[i+nums.lenj1]
其中:

  1. multipliers该选第几个数字,和已经用了几个数字有关。i是左边的数字索引,所以左边用了i个数字,右边数字索引为j,右边用了nums.len - j - 1个数字,这次multipliers应该选第“已经用了的数字个数+1”个数字,数字个数比索引多1,所以其索引恰好就是i + nums.len - j - 1
  2. dp[i][j],有 i ≤ j i \leq j ij,也即矩阵只有上三角;

转移方程有了,接下来是初始化。由于矩阵的每个位置只和左边、下面有关,所以初始化的时候应该从对角线开始
首先考虑n == m的情况,dp[i][i] = nums[i] * multiplliers[-1],填充矩阵时,从下到上,从左到右
接下来再考虑n > m的情况,当m < n时,nums中有一部分数字是完全排不上队和multipliers相乘的,最后一个能和multipliers相乘的数字,其索引应该恰好对应的multipliers的最后一个数字索引,也即i + nums.len - j - 1 == multipliers.len - 1
所以初始化的时候,代码写为:

for i in range(len(nums)):
    for j in range(len(nums)):
        if i + len(nums) - j - 1 == len(multipliers) - 1:
            dp[i][j] = max(nums[i] * multipliers[-1], nums[j] * multipliers[-1])

这样可以写出dp的代码1。

代码的时间复杂度为 o ( n 2 ) o(n^2) o(n2),会超时。

v5
超时了之后我觉得实在没法优化了,就看了看pycharm里dp数组的内容,发现当m < n时,其实dp数组有很大一部分没有填满,只填满了m的部分。
在初始化的时候,只需要初始化对角线,所以初始化可以改成:

# init
for i in range(len(multipliers)):
    j = i + len(nums) - len(multipliers)
    dp[i][j] = max(nums[i] * multipliers[-1], nums[j] * multipliers[-1])

在实际填充时,从初始化的地方开始填充,填充改为:

for i in range(len(multipliers) - 1, -1, -1):
    for j in range(i + len(nums) - len(multipliers) + 1, len(nums)):
        cur_mul = multipliers[i + len(nums) - j - 1]
        dp[i][j] = max(dp[i + 1][j] + nums[i] * cur_mul, dp[i][j - 1] + nums[j] * cur_mul)

这样初始化和填充dp数组的复杂度,都只与m有关,时间复杂度降为 o ( m 2 ) o(m^2) o(m2)

在实际执行时,v5的方案仍然会超时,试了用c++也会超时……可能是因为空间复杂度仍然是 o ( n 2 ) o(n^2) o(n2),理论上来讲,空间复杂度也可以优化到 o ( m 2 ) o(m^2) o(m2),但是我脑子不够用,感觉没法优化了……
倒是发现一个,可以把nums里面排不上队的那些元素去掉,也就是对于nums而言,最多只有左边m个元素和右边m个元素能用上,所以可以把nums中间的那些元素去掉,仅保留左右两边m个元素,这时 n = 2 ∗ m n = 2 * m n=2m,空间复杂度就变成 o ( m 2 ) o(m^2) o(m2)了!


看了大家的做法,其实v1的方法可以稍改一下,也能过

代码

v1

class Solution:
    def maximumScore(self, nums: List[int], multipliers: List[int]) -> int:
        @lru_cache(None)
        def helper(nums: tuple, multipliers: tuple) -> int:
            if not multipliers:
                return 0
            return max(nums[0] * multipliers[0] + helper(nums[1:], multipliers[1:]), nums[-1] * multipliers[0] + helper(nums[:-1], multipliers[1:]))
        return helper(tuple(nums), tuple(multipliers))

v4

class Solution:
    def maximumScore(self, nums: List[int], multipliers: List[int]) -> int:
        dp = [[0] * len(nums) for _ in range(len(nums))]
        # init
        for i in range(len(nums)):
            for j in range(len(nums)):
                if i + len(nums) - j - 1 == len(multipliers) - 1:
                    dp[i][j] = max(nums[i] * multipliers[-1], nums[j] * multipliers[-1])
        for row in range(len(multipliers) - 1, -1, -1):
            for col in range(row + 1, len(nums)):
                cur_mul = multipliers[row + len(nums) - col - 1] if row + len(nums) - col - 1 < len(multipliers) else 0
                dp[row][col] = max(dp[row + 1][col] + nums[row] * cur_mul, dp[row][col - 1] + nums[col] * cur_mul)
        return dp[0][-1]

v5

class Solution:
    def maximumScore(self, nums: List[int], multipliers: List[int]) -> int:
        nums = nums[:len(multipliers)] + nums[-len(multipliers):]
        dp = [[0] * len(nums) for _ in range(len(nums))]
        # init
        for i in range(len(multipliers)):
            j = i + len(nums) - len(multipliers)
            dp[i][j] = max(nums[i] * multipliers[-1], nums[j] * multipliers[-1])
        for i in range(len(multipliers) - 1, -1, -1):
            for j in range(i + len(nums) - len(multipliers) + 1, len(nums)):
                cur_mul = multipliers[i + len(nums) - j - 1]
                dp[i][j] = max(dp[i + 1][j] + nums[i] * cur_mul, dp[i][j - 1] + nums[j] * cur_mul)
        return dp[0][-1]

v1修改版

class Solution:
    def maximumScore(self, nums: List[int], multipliers: List[int]) -> int:
        n, m = len(nums), len(multipliers)
        @lru_cache(n)
        def helper(left: int, right: int, mul_index: int) -> int:
            if mul_index == m:
                return 0
            a1 = nums[left] * multipliers[mul_index] + helper(left + 1, right, mul_index + 1)
            a2 = nums[right] * multipliers[mul_index] + helper(left, right - 1, mul_index + 1)
            return max(a1, a2)
        return helper(0, n - 1, 0)

5688. 由子序列构造的最长回文串的长度

这道题没做出来,赛后做的

题目

给你两个字符串 word1 和 word2 ,请你按下述方法构造一个字符串:

从 word1 中选出某个 非空 子序列 subsequence1 。
从 word2 中选出某个 非空 子序列 subsequence2 。
连接两个子序列 subsequence1 + subsequence2 ,得到字符串。
返回可按上述方法构造的最长 回文串 的 长度 。如果无法构造回文串,返回 0 。

字符串 s 的一个 子序列 是通过从 s 中删除一些(也可能不删除)字符而不更改其余字符的顺序生成的字符串。

回文串 是正着读和反着读结果一致的字符串。

示例 1:

输入:word1 = "cacb", word2 = "cbba"
输出:5
解释:从 word1 中选出 "ab" ,从 word2 中选出 "cba" ,得到回文串 "abcba" 。

示例 2:

输入:word1 = "ab", word2 = "ab"
输出:3
解释:从 word1 中选出 "ab" ,从 word2 中选出 "a" ,得到回文串 "aba" 。

示例 3:

输入:word1 = "aa", word2 = "bb"
输出:0
解释:无法按题面所述方法构造回文串,所以返回 0 。

提示:

1 <= word1.length, word2.length <= 1000
word1 和 word2 由小写英文字母组成

解题思路

参考:https://leetcode-cn.com/problems/maximize-palindrome-length-from-subsequences/solution/zui-chang-hui-wen-zi-xu-lie-xian-zhi-tia-g6nx/

类似最长回文子序列

word1word2合并到一起,记合并完的字符串为word,用dp[i][j]表示word[i:j]的回文子序列长度,则转移方程:
d p [ i ] [ j ] = { 2 + d p [ i + 1 ] [ j − 1 ] , i f      w o r d [ i ] = = w o r d [ j ] m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) , e l s e . \begin{aligned} dp[i][j] = \begin{cases} 2 + dp[i + 1][j - 1], &if \;\; word[i] == word[j] \\ max(dp[i + 1][j], dp[i][j - 1]), &else. \end{cases} \end{aligned} dp[i][j]={ 2+dp[i+1][j1],max(dp[i+1][j],dp[i][j1]),ifword[i]==word[j]else.
在更新答案时,需要保证i属于word1长度范围内,j属于word2长度范围内,并且word[i] == word[j]。最后一个条件可以保证word1word2都至少有字符被取到了

代码

class Solution:
    def longestPalindrome(self, word1: str, word2: str) -> int:
        l1, l2 = len(word1), len(word2)
        new_word = word1 + word2
        l = l1 + l2
        dp = [[0] * l for _ in range(l)]
        # init
        for i in range(l):
            dp[i][i] = 1
        # dp
        ans = 0
        for i in range(l - 1, -1, -1):
            for j in range(i + 1, l):
                if new_word[i] == new_word[j]:
                    dp[i][j] = 2 + (dp[i + 1][j - 1] if i <= j - 2 else 0)
                    if i < l1 <= j:
                        ans = max(ans, dp[i][j])
                else:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])                
        return ans

你可能感兴趣的:(OJ题目记录)