题目:
给你一个整数数组 nums
(下标从 0 开始)和一个整数 k
。
一个子数组 (i, j)
的 分数 定义为 min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1)
。一个 好 子数组的两个端点下标需要满足 i <= k <= j
。
请你返回好子数组的最大可能分数 。
思考:
1. 最直接的解法依然是遍历,分别用i和j指向数组头和尾,计算每一个子数组的分数并与上一个分数比较,若更大则代替。代码如下:
class Solution(object):
def maximumScore(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
n = len(nums)
i = 0
j = n-1
score = 0
while(i <= k <= j):
for i in range(0, k+1):
new_score = min([nums[x] for x in range(i, j+1)]) * (j-i+1)
if new_score > score:
score = new_score
j -= 1
return score
显而易见的超时了T_T:
可以看到上述解法是两个循环嵌套,时间复杂度为O(n²)。
2. 既然子数组必须包含nums[k],那么我们以 nums[k]为中心,向左/右逐个增加元素扩充子数组,每增加一个元素就判断一次分数是否变大。
由分数的计算公式可知,我们要尽可能:1.使子数组中包含大的数;2.使子数组的元素尽可能多。
那么我们在每次扩充子数组时,选择相邻元素中更大的数加入子数组。直到子数组扩展至与原数组相同为止,结束循环。
代码如下,时间复杂度为O(n):
class Solution(object):
def maximumScore(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
n = len(nums)
i = k-1
j = k+1
# 从k的位置向左右扩充子数组
score = 0
x = nums[k]
# x代表当前子数组中最小值
while True:
# 若左/右数大于等于x,则直接加入子数组,因为其值对分数没有影响
while(i >= 0 and nums[i] >= x):
i -= 1
while(j <= n-1 and nums[j] >= x):
j += 1
# 每次循环更新最大分数
# 由于此处的i比题意中i小1,j比题意中大1,所以公式不是min*(j-i+1),而是min*(j-i-1)
score = max(score, x*(j-i-1))
# 目前左右数都小于x或没有数
# 若左右都没有数则退出循环,否则选相对更大的数加入子数组
x = max((-1 if i == -1 else nums[i]), (-1 if j == n else nums[j]))
if x == -1:
break
return score
运行通过: