Leetcode刷题记录 day4

本文介绍了解决一个编程问题的方法,即在一个整数数组中找到以整数k为中心的好子数组,其分数由子数组内最小值乘以长度计算。作者通过分析提出从两端向中心逐步推进的策略,最终实现O(n)的时间复杂度和O(1)的空间复杂度的解决方案。
摘要由CSDN通过智能技术生成

每日一题 好子数组的最大分数

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

一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1) 。一个  子数组的两个端点下标需要满足 i <= k <= j 。

请你返回  子数组的最大可能 分数 。

解题思路

从题目入手,最直观的方法便是根据k的值,将整数数组nums分为左右两个子数组,并依次遍历两个数组,计算出分数的最大值。但是这种方法需要的时间复杂度达到了O(n2),不太符合一道困难题该有的样子。

在草稿纸上写了些码了下字就看不懂的东西后,得到了一个思路

我们需要获得好子数组的最大分数,按照公式,自然是要将子数组内的最小值“最大化”的同时,让子数组的长度最大。我们有可能会想到先获取到原数组内最小值的位置,但经过观察就可以发现,取到最大分数的子数组通常是将最两边的一些“最小值”舍弃了的,因此获取最小值的意义就没有那么大了,因为我们可能还是要遍历完整个数组才能确定最大分数。

那么如果反过来呢?我们首先各获取到k两边子数组的最大值及其位置,然后...等等就算我获取到了最大值,可他取不到啊,比如上面的数组,我获取到了最大元素“4”和“5”以及他们的下标,可实际上计算时左边取的应该是“3”。但是我又注意到,到了“4”之后,左边的数组再往右缩就没有必要了,因为最小值不会改变,而“好子数组”的长度会缩短,最大分数会减小。

这个时候,我又灵光一闪,那我们可以从中间开始向两边推啊emmm,也不现实,如果某靠边的数字特别大...哦再大也是取最小值啊,所以如果在往两边走的过程中,好子数组的分数减小了,那就没必要再继续下去了...吗?如果在走的过程中最小值减小导致整体分数下降,但是之后元素保持不变,只要不变的长度够长,分数也会反超。这就是困难题的含金量吗!

没办法,先试试暴力解法能不能过吧

class Solution:
    def maximumScore(self, nums: List[int], k: int) -> int:
        left = nums[:k+1]
        right = nums[k:]
        max_score = 0
        left_index = 0
        for i in left:
            right_idex = k
            for j in right:
                if left_index != right_idex:
                    score = min(nums[left_index:(right_idex+1)]) * (right_idex - left_index + 1)
                else:
                    score = nums[left_index]
                max_score = max(max_score, score)
                right_idex += 1
            left_index += 1
        
        return max_score

果然啊,在数据量小的时候还是可以的,数组一大就寄了。

没什么思路了,看了一眼官方题解,发现原来跟我之前想到的从中间(k)向两边推的思路是一样的,它会先以nums[k]作为子数组中的最小值,然后让nums[k]与左右的元素比较,直到元素比nums[k]的值还小(这就说明分数的计算中,“min(num[i:j+1]”的值需要更新,在更新之前肯定是数组越长,分数越高(因此在更新之前的分数也没必要计算了))。然后将i值更新,重复上述操作,并与已有的最大分数进行比较并更新最大值,直到左右指针到达尽头。

原来跟我上面的思路是差不多的,有点可惜,就差一点就能自己解决了。

代码实现

class Solution:
    def maximumScore(self, nums: List[int], k: int) -> int:
        n = len(nums)
        left, right, i = k - 1, k + 1, nums[k]
        ans = 0
        while True:
            while left >= 0 and nums[left] >= i:
                left -= 1
            while right < n and nums[right] >= i:
                right += 1
            ans = max(ans, (right - left - 1) * i)
            i = max((-1 if left == -1 else nums[left]), (-1 if right == n else nums[right]))
            if i == -1:
                break
        return ans

复杂度分析

时间O(n),空间O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值