给你一个整数数组 nums (下标从 0 开始)和一个整数 k 。
一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i+1], …, nums[j]) * (j - i + 1) 。一个 好 子数组的两个端点下标需要满足 i <= k <= j 。
请你返回 好 子数组的最大可能 分数 。
示例 1:
输入:nums = [1,4,3,7,4,5], k = 3
输出:15
解释:最优子数组的左右端点下标是 (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
思路:
(1)首先我第一个想到的又是暴力解法,直接三个循环嵌套,不出意外的超时了
代码如下:
class Solution(object):
def maximumScore(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
n = len(nums)
Result = 0
for i in range(0,k+1):
for j in range(k,n):
m = nums[i]
for x in range(i,j+1):
if m > nums[x]:
m = nums[x]
s = m * (j-i+1)
if s > Result:
Result = s
return Result
(2)现在想想咋降低时间复杂度呢?
因为好子数组要求i<=k<=j,可以发现,不管怎么选择好子数组的范围,nums[k]永远在好子数组内。
那么我们可以使用双指针,一个指向k的前一位,一个指向k的后一位。
枚举法,首先令i=nums[k]作为当前好子数组的最小值,在left和right不超出边界的情况下:若k左边的数>=nums[k],则left-1,若k右边的数>=nums[k],则right+1。最后得出的i*(right-left-1)就是nums[k]作为最小值的好子数组的最大分数。然后用i*(right-left-1)与result比大小,如果更大,则替换result。
然后令i-1,继续计算最大值,与result比较,更新即可。
这样时间复杂度减少了
Python代码:
class Solution(object):
def maximumScore(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
n = len(nums)
left = k - 1
right = k + 1
result = 0
for i in range(nums[k],0,-1): #因为nums[k]一定包含在好子数组中,用双指针从中间开始遍历,
while left >= 0 and nums[left] >=i: #第一轮i=nums[k]计算出nums[k]为最小值时,好字子数组的最大分数
left -= 1
while right < n and nums[right] >= i:
right +=1
result = max(result,i*(right - left - 1))#然后最小值逐渐递减到1,求出每个最小值对应的最大分数
return result #不断更新最大分数,返回最大的
(3)刚刚的解法中,每一次更新i,left和right有可能不会移动,这样浪费了时间。如果我们每一次更新i的时候不让i-1了,而是让他等于nums[left]和nums[right]中的最大值,这样就可以 保证每一次更新i,left和right都有移动,再一次减少的运行时间。
Python代码:
class Solution(object):
def maximumScore(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
n = len(nums)
left = k - 1
right = k + 1
result = 0
i = nums[k]
while True: #因为nums[k]一定包含在好子数组中,用双指针从中间开始遍历,
while left >= 0 and nums[left] >=i: #第一轮i=nums[k]计算出nums[k]为最小值时,好字子数组的最大分数
left -= 1
while right < n and nums[right] >= i:
right +=1
result = max(result,i*(right - left - 1))#然后最小值逐渐递减到1,求出每个最小值对应的最大分数
i = max(-1 if left == -1 else nums[left],-1 if right == n else nums[right])
if i<=0:
break
return result #不断更新最大分数,返回最大的
------改进算法参考LeetCode--------