You are given an array of integers nums(0-indexed) and an integer k.
The score of a subarray (i, j) is defined as min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1). A good subarray is a subarray where i <= k <= j.
Return the maximum possible score of a good subarray.
Example 1:
Input: nums = [1,4,3,7,4,5], k = 3
Output: 15
Explanation: The optimal subarray is (1, 5) with a score of min(4,3,7,4,5) * (5-1+1) = 3 * 5 = 15.
Example 2:
Input: nums = [5,5,4,5,4,1,1,1], k = 0
Output: 20
Explanation: The optimal subarray is (0, 4) with a score of min(5,5,4,5,4) * (4-0+1) = 4 * 5 = 20.
Constraints:
1 <= nums.length <= 105
1 <= nums[i] <= 2 * 104
0 <= k < nums.length
Solutions
Solution 1: Monotonic Stack
Thinking
A good subarray must cover index \(k\); its score is length times the minimum. \(n\le 10^5\) forbids trying every window that covers \(k\).
Treat each index as the minimum: a monotonic stack finds the first strictly smaller values on both sides. If that span still covers \(k\), update with \(v\times\textit{width}\).
We can enumerate each element \(nums[i]\) in \(nums\) as the minimum value of the subarray, and use a monotonic stack to find the first position \(left[i]\) on the left that is less than \(nums[i]\) and the first position \(right[i]\) on the right that is less than or equal to \(nums[i]\). Then, the score of the subarray with \(nums[i]\) as the minimum value is \(nums[i] \times (right[i] - left[i] - 1)\).
It should be noted that the answer can only be updated when the left and right boundaries \(left[i]\) and \(right[i]\) satisfy \(left[i]+1 \leq k \leq right[i]-1\).
The time complexity is \(O(n)\), and the space complexity is \(O(n)\). Here, \(n\) is the length of the array \(nums\).
The stack uses linear extra memory. Two pointers starting at \(k\) can grow the window, always extending toward the larger neighbour so the running minimum falls as slowly as possible, updating \(\textit{min}\times\textit{len}\) as they go. Extra space is \(O(1)\).
We can initialize two pointers at the core index k and expand outward to the left and right. By maintaining the minimum value within current window, we can find maximum score in strict linear time.
Algorithm Steps:
Initialize left pointer i = k, right pointer j = k, and window minimum value min_num = nums[k]. Set initial maximum score max_score = nums[k].
Expand pointers while i > 0 or j < len(nums) - 1:
Direction: If left boundary can't expand (i == 0), move right pointer j++. If right boundary can't expand (j == len(nums) - 1), move left pointer i--.
If both sides are expandable, compare nums[i - 1] and nums[j + 1], and expand towards the side with a larger value (i.e., if nums[i - 1] >= nums[j + 1], decrement i; otherwise, increment j).
Update State: after each pointer movement, update current window minimum value: min_num = min(min_num, nums[i] or nums[j]).
Calculate Score: length of the current good subarray is j + 1 - i, and its score is score = min_num * (j + 1 - i). Update global maximum score: max_score = max(max_score, score).
Return max_score once the for loop terminates.
Complexity Analysis:
Time Complexity: \(O(n)\), where \(n\) is length of array nums. Each element is scanned at most once.
Space Complexity: \(O(1)\), as it only requires a constant amount of extra space for two pointers, window minimum value and global maximum score.
classSolution:defmaximumScore(self,nums:list[int],k:int)->int:max_score=nums[k]# Base case.min_num=nums[k]left_idx,right_idx=k,kwhile0<left_idxorright_idx<len(nums)-1:ifleft_idx==0:# Can only go right.right_idx+=1min_num=min(min_num,nums[right_idx])elifright_idx==len(nums)-1:# Can only go left.left_idx-=1min_num=min(min_num,nums[left_idx])else:# Can go bidirectional.ifnums[left_idx-1]>=nums[right_idx+1]:left_idx-=1min_num=min(min_num,nums[left_idx])else:right_idx+=1min_num=min(min_num,nums[right_idx])score=min_num*(right_idx+1-left_idx)max_score=max(max_score,score)returnmax_score
classSolution{public:intmaximumScore(vector<int>&nums,intk){intmaxScore=nums[k],minNum=nums[k];// Base case.intleftIdx=k,rightIdx=k;while(0<leftIdx||rightIdx<nums.size()-1){if(leftIdx==0){// Can only go right.rightIdx++;minNum=min(minNum,nums[rightIdx]);}elseif(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]);}}intscore=minNum*(rightIdx+1-leftIdx);maxScore=max(maxScore,score);}returnmaxScore;}};