跳到主要内容

1793. Good Subarray Score

Maximum Score of a Good Subarray

从本题对于好的子数组的左右边界描述:

左边界 k\leq k \leq 右边界

我们可看出 所有待搜查的子数组 都必须有nums[k]nums[k]

既然老𝓚肯定得存在

何不就让我们把位nums[k]nums[k]设为搜查中心?

开俩根指针:leftidxrightIdx

一个负责从kk递减扫向左尾 一个负责从kk递增扫向右尾

每次的扫描 可分三种情形:

I. leftidxrightIdx都没有越界

比较nums[leftidx]nums[rightIdx]的大小

较大的那个会成为当前子数组的延伸对象 就是新成员

选较大的的原因是 子数组的分数 = 子数组最小值 * 子数组长度

选较大者自然能尽量维持子数组的最小值 或者降低下滑量

II. leftidx越界 只能请rightIdx递增

III. rightIdx越界 只能请leftidx递减

如此的移动 遍能覆盖全部带有nums[k]nums[k]的子数组啰

仅需于每次移动后 计算当前子数组的分数

和历史最高分数maxScore比较 做出必要的更新即可

Two Pointers_Efficiency

时间复杂度是O(n)O(n) 我们所有数字都被访问了刚好一次

空间复杂度能压到O(1)O(1) 因为我们只需要两根指针

当前子数组最小值 子数组分数 还有历史最高分数 五变量搞定

题外话 如上的贪心双指针解方 后来被我发起pull request

合并到LeetCode Wiki 把空间复杂度从O(n)O(n)调到O(1)O(1)

#include <vector>
using namespace std;

int findMaxScore(vector<int>& nums, int k) // LeetCode Q.1793.
{
int maxScore = nums[k], minNum = nums[k]; // Base case.

int leftIdx = k, rightIdx = k;

while (0 < leftIdx || rightIdx < nums.size() - 1) {
if (leftIdx == 0) { // Can only go right.
rightIdx++;
minNum = min(minNum, nums[rightIdx]);
}

else if (rightIdx == nums.size() - 1) { // Can only go left.
leftIdx--;
minNum = min(minNum, nums[leftIdx]);
}

else { // Can go bidirectional.
if (nums[leftIdx - 1] >= nums[rightIdx + 1]) {
leftIdx--;
minNum = min(minNum, nums[leftIdx]);
}

else {
rightIdx++;
minNum = min(minNum, nums[rightIdx]);
}
}

int score = minNum * (rightIdx + 1 - leftIdx);
maxScore = max(maxScore, score);
}

return maxScore;
}