leetcode 第 221 场周赛

5629.重新格式化电话号码

题目描述

给你一个字符串形式的电话号码 number 。number 由数字、空格 ’ '、和破折号 ‘-’ 组成。

请你按下述方式重新格式化电话号码。

首先,删除 所有的空格和破折号。
其次,将数组从左到右 每 3 个一组 分块,直到 剩下 4 个或更少数字。剩下的数字将按下述规定再分块:
2 个数字:单个含 2 个数字的块。
3 个数字:单个含 3 个数字的块。
4 个数字:两个分别含 2 个数字的块。
最后用破折号将这些块连接起来。注意,重新格式化过程中 不应该 生成仅含 1 个数字的块,并且 最多 生成两个含 2 个数字的块。

返回格式化后的电话号码。

示例

输入:number = “1-23-45 6” 输出:“123-456” 解释:数字是 “123456” 步骤 1:共有超过 4
个数字,所以先取 3 个数字分为一组。第 1 个块是 “123” 。 步骤 2:剩下 3 个数字,将它们放入单个含 3 个数字的块。第 2
个块是 “456” 。 连接这些块后得到 “123-456” 。

思路

分为两步做,第一步,删除 “空格” 和 “-”,第二部,转换格式

第一步,简单
第二部分为三种情况:

  • 电话号码总数为3*n ,对应格式xxx-xxx-xxx
  • 电话号码总数为3*n+1 ,对应格式xxx-xxx-xx-xx
  • 电话号码总数为3*n+2,对应格式xxx-xxx-xxx-x

代码

class Solution:
    def reformatNumber(self, number: str) -> str:
        if len(number) <= 3:
            return number
        # del
        clean_nums = []
        for n in number:
            if '0' <= n <= '9':
                clean_nums.append(n)
        # connect
        if len(clean_nums) % 3 == 0:
            res = ''.join(clean_nums[0:0+3])
            for i in range(3,len(clean_nums), 3):
                res +=  '-' + ''.join(clean_nums[i:i+3])
            return res
        if len(clean_nums) % 3 == 1:
            i = 0
            res = ''
            while i < len(clean_nums) - 4:
                res +=  ''.join(clean_nums[i:i+3]) + '-'
                i += 3
            res += ''.join(clean_nums[i:i+2]) + '-' + ''.join(clean_nums[i+2:i+4])
            return res
        if len(clean_nums) % 3 == 2:
            i = 0
            res = ''
            while i < len(clean_nums) - 2:
                res +=  ''.join(clean_nums[i:i+3]) + '-'
                i += 3
            res += ''.join(clean_nums[i:i+2])
            return res

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

5630.删除子数组的最大得分

题目描述

给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组。删除子数组的 得分 就是子数组各元素之 和 。

返回 只删除一个 子数组可获得的 最大得分 。

如果数组 b 是数组 a 的一个连续子序列,即如果它等于 a[l],a[l+1],…,a[r] ,那么它就是 a 的一个子数组

示例

输入:nums = [4,2,4,5,6]

输出:17

解释:最优子数组是 [2,4,5,6]

思路

题目不讲武德,浪费了一点时间,一句话说明题意,无重复数字的连续子数组最大和
使用双指针,确定第一个重复数字的右边界,左边慢慢往右边去。

代码

class Solution:
    def maximumUniqueSubarray(self, nums: List[int]) -> int:     
        # 求连续不重复子数组最大和
        num_index = collections.defaultdict(int)
        res = 0
        i = 0
        j = 0
        tmp = 0
        for i in range(len(nums)):
            while j < len(nums) and num_index[nums[j]] == 0:
                tmp += nums[j]
                num_index[nums[j]] += 1
                j += 1
            res = max(tmp, res)
            tmp -= nums[i]
            num_index[nums[i]] -= 1
        return res

复杂度分析

  • 时间复杂度:O(n),虽然有一个内层while,但是while总共只进行n次,与外层n无关
  • 空间复杂度:O(n)

5631. 跳跃游戏 VI

题目描述

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。

一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1,
min(n - 1, i + k)] 包含 两个端点的任意位置。

你的目标是到达数组最后一个位置(下标为 n - 1 ),你的 得分 为经过的所有数字之和。

请你返回你能得到的 最大得分 。

示例

输入:nums = [1,-1,-2,4,-7,3], k = 2 输出:7 解释:你可以选择子序列 [1,-1,4,3]
(上面加粗的数字),和为 7 。

思路1

看到题目,第一想法就是动态规划
dp[i] = max(dp[i-k], dp[i-k+1],…,dp[i-1])

代码

class Solution:
    def maxResult(self, nums: List[int], k: int) -> int:
        dp = [-float('inf')] * len(nums)
        dp[0] = nums[0]
        for i in range(1, len(nums)):
            dp[i] = max(dp[max(0,i-k):i]) + nums[i]
        return dp[-1]

复杂度分析

  • 时间复杂度:O(n*k)
  • 空间复杂度:O(n)

超时分析

对于dp[i],会计算dp[i-k],…,dp[i-1]
对于dp[i+1],会计算dp[i-k+1],…,dp[i]



发现每个元素都被计算了k次,
如果能直接把前面k个窗口的值保存下来就好了,单调队列值得拥有

代码

class Solution:
    def maxResult(self, nums: List[int], k: int) -> int:
        dp = [-float('inf')] * len(nums)
        maxWindow = deque()  # 单调队列
        for i in range(len(nums)):
            while maxWindow and i - k > maxWindow[0]:
                maxWindow.popleft()
            dp[i] = dp[maxWindow[0]] + nums[i] if maxWindow else nums[i]
            while maxWindow and dp[i] > dp[maxWindow[-1]]:
                maxWindow.pop()
            maxWindow.append(i)
        return dp[-1]

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值