5704. 好子数组的最大分数

题目

给你一个整数数组 nums (下标从 0 开始)和一个整数 k
一个子数组 (i, j)分数 定义为 min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1) 。一个 子数组的两个端点下标需要满足 i <= k <= j
请你返回 子数组的最大可能 分数
示例 1:

  1. 输入:nums = [1,4,3,7,4,5], k = 3
  2. 输出:15
  3. 解释:最优子数组的左右端点下标是 (1, 5) ,分数为 min(4,3,7,4,5) * (5-1+1) = 3 * 5 = 15

示例 2:

输入:nums = [5,5,4,5,4,1,1,1], k = 0
输出:20
解释:最优子数组的左右端点下标是 (0, 4) ,分数为 min(5,5,4,5,4) * (4-0+1) = 4 * 5 = 20 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 2 * 104
  • 0 <= k < nums.length

题解

这道题和 11. 盛最多水的容器 非常类似,所以我第一时间就想到的是贪心法。

首先是设好双指针 i, j,显然双指针只能从中间往外移动,然后思考根据什么规则来移动指针。
比较 nums[i]nums[j] 的大小是没有意义的,因为我们完全可以把 ij 之间的值全部看做最小值 min_num,超出最小值的部分是没有意义的。
真正影响的,是指针两端的值,也就是 nums[i-1]nums[j+1] 的大小。

所以我们很直观的思路就是将指针往大的那一方向扩张。
那么这种策略有没有可能漏掉最优解的情况呢?

5704. 好子数组的最大分数 - 图1

现在我们假设最优情况为 (i*,j*),那么由于指针从中间往外扩张,必然有一个指针先到达最优位置,我们假设右指针 j 已经到达了 j*,现在左指针 i 还在 k 处。

那么我们首先可以肯定:j*+1 处的值,比 i*j* 之间所有的值都要小。否则 (i*,j*+1) 就是比 (i*,j*) 更优的解,这与假设矛盾。

所以此时我们比较 nums[i-1]nums[j+1] 的大小时,肯定是左边的更大,因此左指针 i 往左移。
左移之后,还是面临同样的情况,因此在左指针 i 到达 i* 之前,移动的一定是左指针,也就是说该策略一定能匹配到最优解。

最终代码如下:

class Solution:
    def maximumScore(self, nums, k):
        n = len(nums)
        min_num = nums[k]
        max_value = min_num
        i = k
        j = k
        while i > 0 or j < n-1:
            if i == 0:
                j += 1
            elif j == n-1:
                i -= 1
            elif nums[i-1] > nums[j+1]:
                i -= 1
            else:
                j += 1
            min_num = min(min_num, min(nums[i], nums[j]))
            max_value = max(max_value, min_num*(j - i + 1))
        return max_value

image.png