欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。
题目描述
[1793] 好子数组的最大分数
- https://leetcode-cn.com/problems/maximum-score-of-a-good-subarray/
给你一个整数数组 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 <= 10^5
1 <= nums[i] <= 2 * 10^4
0 <= k < nums.length
- 贪心
- 单调栈
- 枚举
题目剖析&信息挖掘
先通过朴素思想设计出算法框架,然后针对算法中的核心过程进行优化。
解题思路
方法一 贪心+最小栈+枚举
解析
以例1为例。
nums = [1,4,3,7,4,5], k = 3
每次枚举i, 以nums[i]为最小值,查看i向左右延伸,延伸的下标记为x, y。
以i=2为例,用3向左,右扩展,只要比自己大或等于,就往外扩。最终X=1, Y=5。
整体枚举过程如下表。
如x<=k && k<=y 就把 nums[i]*(y-x+1)记为可行解。最终答案 为 max(nums[i]*(y-x+1))
黄色行为最佳答案。
复杂度分析,由于每次计算x, y都要O(n)复杂度,最终复杂度为O(n^2)。
优化
上述的关键点在于x,y的计算。
定义left[],right[] ,x=left[i], y=right[i]
如果可以找到一种方法,去快速计算出数组left[],right[]。那么问题应该可以解决。
由于left, right只是方向不同,计算逻辑应该是一样。所以先看left
可以先尝试几种情况
left[0]=0 这是肯定的,左边没有元素了。
当i>0时。如果nums[i]>nums[i-1], left[i]=i。
nums[i]==nums[i-1] , left[i]=left[i-1] 数字一样,向左延伸肯定也是一样的。
nums[i]<nums[i-1], 要向左寻找第一个小于自己的数字下标j, left[i]=left[j+1]
以上图为例计算left[4],
left[0]=0, left[1]=1, left[2]=1,left[3]=3已经计算好了。
最终j=0, left[4]=left[1]=1。
比较数字分别(从右到左)是[ 7,3,4,1].
可以发现4比3大,当比较nums[2]与nums[4]时,就已经知道nums[1]>nums[4]。完全是可以省略的。
这里可以引入单调栈数据结构。此栈如其名,就是栈里的数据就是单调递增的。
复杂度,由于每个元素最多入栈出栈一次,所以总体复杂度是O(n)
思路
func maximumScore(nums []int, k int) int {
left := make([]int, len(nums))
right := make([]int, len(nums))
stack := []int{}
for i, n := range nums {
left[i]=-1
for len(stack)>0 {
lastInd := len(stack)-1
if nums[stack[lastInd]] < n{
break
}
left[i]=left[stack[lastInd]] // 更新答案,当遇到nums[stack[lastInd]] < n时 left[i] = left[stack[lastInd]]
stack = stack[:lastInd]
}
if left[i]==-1 { // 说明栈中没有自己大的。
left[i]=i
}
stack = append(stack, i)
}
for i:=len(nums)-1;i>=0;i-- {
// 同上
}
best :=0
for i:=0;i<len(nums);i++ {
if left[i]<=k && right[i]>=k {
best = max(best, (right[i]-left[i]+1) * nums[i])
}
}
return best
}
注意
- 当栈为空时,left[i], right[i]=i
知识点
- 贪心
- 单调栈
- 枚举
复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(n)
代码实现
func max(a, b int) int {
if a > b {
return a
}
return b
}
func maximumScore(nums []int, k int) int {
left := make([]int, len(nums))
right := make([]int, len(nums))
stack := []int{}
for i, n := range nums {
left[i]=-1
for len(stack)>0 {
lastInd := len(stack)-1
if nums[stack[lastInd]] < n{
break
}
left[i]=left[stack[lastInd]]
stack = stack[:lastInd]
}
if left[i]==-1 {
left[i]=i
}
stack = append(stack, i)
}
stack = []int{}
for i:=len(nums)-1;i>=0;i-- {
right[i]=-1
for len(stack)>0 {
lastInd := len(stack)-1
if nums[stack[lastInd]] < nums[i]{
break
}
right[i]=right[stack[lastInd]]
stack = stack[:lastInd]
}
if right[i]==-1 {
right[i]=i
}
stack = append(stack, i)
}
best :=0
for i:=0;i<len(nums);i++ {
if left[i]<=k && right[i]>=k {
best = max(best, (right[i]-left[i]+1) * nums[i])
}
}
return best
}
本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。