From d9defee68e45806f9fdc5ec400cd4b77ee9b6c0b Mon Sep 17 00:00:00 2001 From: Jack Yao <105488074+StarsExpress@users.noreply.github.com> Date: Mon, 11 May 2026 12:16:59 -0400 Subject: [PATCH 1/2] Added No.3430 O(n) Python & C++ solutions. --- .../README.md | 202 +++++++++++++++++- .../README_EN.md | 166 +++++++++++++- .../Solution.cpp | 73 +++++++ .../Solution.py | 57 +++++ 4 files changed, 484 insertions(+), 14 deletions(-) create mode 100644 solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp create mode 100644 solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py diff --git a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README.md b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README.md index 70974e17fd46b..688c1ec398bfa 100644 --- a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README.md +++ b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README.md @@ -164,32 +164,220 @@ tags: -### 方法一 +### 方法一:双Deque分管最大值和最小值 +本题要的总值$S = MaxSum_i + MinSum_i$: + +I. $MaxSum_i = \Sigma_{j = max(0, i - k + 1)}^i max(nums[j: i + 1])$ + +索引$i$作结尾的所有合法子数组 各自最大值和 我们拿```subarrays_max_sum```表示$MaxSum_i$ + +II. $MinSum_i = \Sigma_{j = max(0, i - k + 1)}^i min(nums[j: i + 1])$ + +索引$i$作结尾的所有合法子数组 各自最小值和 我们拿```subarrays_min_sum```表示$MinSum_i$ + +同时我们储备两个Deque:```max_stack```和```min_stack``` + +来分管索引$i$作子数组结尾时 所有长度不超过$k$的子数组 各自的最大值和最小值是谁 + +虽然数据结构是双端队列 但命名和本质还是叫栈 + +因为Push和Pop绝大多数都发生在右侧 左边仅是为了控制边界 + +两个队列内所有元素 都是长度为三的列表 列表内由左到右代表了 + +__索引、数值、该数在当前窗口内作为子数组最大值/最小值的次数__ + +#### Step 1. 边界管制 +迭代过程轮到$nums[i]$时 我们都要计算 + +由索引$i$作为结尾的所有合法子数组的最大值和最小值之和 + +因此首先检查 __$max(0, i - k + 1)$__ 是否大于零 + +大于零 说明子数组$nums[i - k:i]$会因$nums[i]$进入而长度超标$k$ + +于是$nums[i - k:i]$这个子数组贡献的最大值和最小值就要被剔除掉了 + +$nums[i - k:i]$的最大值和最小值 就在```max_stack```和```min_stack```的栈头了 + +于是我们把```max_stack```和```min_stack```栈头元素的贡献各扣一 + +要是扣完后发现栈头元素的索引 < $max(0, i - k + 1)$ + +代表栈头元素那数值已经掉出窗口 就能弹掉栈头方便计数和管理效率 + +#### Step 2. 比较单调栈顶 +只要```max_stack```不为空 且```max_stack```栈顶数值 $\leq nums[i]$ (1) + +说明索引$i$作为结尾的所有合法子数组 最大值就完全不可能是```max_stack```栈顶数值了 + +此时$nums[i]$就要把```max_stack```栈顶元素贡献的最大值次数接手过来 拿给$nums[i]$用 + +若栈顶元素数值叫做```prev_num``` 贡献的最大值次数叫做```prev_shares``` + +会对```subarrays_max_sum```产生$(nums[i] - \text{prev_num}) * \text{prev_shares}$的净增量 + +同时我们还有$nums[i]$单独成立的子数组 ```subarrays_max_sum```得再添加$nums[i]$ + +把弹栈操作好 ```subarrays_max_sum```更新完 + +就轮到$nums[i]$带著自己的索引$i$ 和负责的最大值次数入```max_stack```了 + +```min_stack```的操作和```max_stack```的操作完全一样 + +只是比较条件(1)要反过来 改成```min_stack```栈顶数值 $\geq nums[i]$ (2) + +每次迭代$nums[i]$到尾声时 再把```subarrays_max_sum```和```subarrays_min_sum``` + +一起加到总和```subarrays_max_min_sum```上 + +最后要回传的便是这个```subarrays_max_min_sum``` + +时间复杂度 $O(n)$,其中 $n$ 为数组 $\textit{nums}$ 的长度。 + +这是由于每个元素最多被压入和弹出两个栈合计四次。 + +空间复杂度 $O(n)$。 #### Python3 ```python +class Solution: + def minMaxSubarraySum(self, nums: list[int], k: int) -> int: + subarrays_max_min_sum = 0 -``` + max_stack: deque[list[int]] = deque([]) # Format: [idx, num, shares]. + subarrays_max_sum = 0 + + min_stack: deque[list[int]] = deque([]) # Format: [idx, num, shares]. + subarrays_min_sum = 0 + + for end_idx, num in enumerate(nums): + start_idx = max(0, end_idx - k + 1) + + # Window start idx slides by 1: must update stacks' info. + if start_idx > 0: + max_stack[0][2] -= 1 # Decrement stack's front num shares. + subarrays_max_sum -= max_stack[0][1] + + if max_stack[0][0] < start_idx: # Front num out of window. + max_stack.popleft() + + min_stack[0][2] -= 1 # Decrement stack's front num shares. + subarrays_min_sum -= min_stack[0][1] + + if min_stack[0][0] < start_idx: # Front num out of window. + min_stack.popleft() + + max_shares = 1 # Base case. + subarrays_max_sum += num + + while max_stack and max_stack[-1][1] <= num: + _, prev_num, prev_shares = max_stack.pop() + + max_shares += prev_shares # Max shares transition. + + # Reflect transition in max sum. + subarrays_max_sum += (num - prev_num) * prev_shares + + max_stack.append([end_idx, num, max_shares]) + + min_shares = 1 # Base case. + subarrays_min_sum += num + + while min_stack and min_stack[-1][1] >= num: + _, prev_num, prev_shares = min_stack.pop() + + min_shares += prev_shares # Min shares transition. -#### Java + # Reflect transition in min sum. + subarrays_min_sum += (num - prev_num) * prev_shares -```java + min_stack.append([end_idx, num, min_shares]) + subarrays_max_min_sum += subarrays_max_sum + subarrays_min_sum + + return subarrays_max_min_sum ``` #### C++ ```cpp +class Solution { +public: + long long minMaxSubarraySum(vector& nums, int k) { + long long totalMaxMinSum = 0; + long long windowMaxSum = 0, windowMinSum = 0; + + // Format: {idx, num, shares}. Use long long to prevent overflow. + deque> maxStack, minStack; + + for (int endIdx = 0; endIdx < nums.size(); endIdx++) + { + int startIdx = max(0, endIdx - k + 1); + + // Window start idx slides by 1: must update stacks' info. + if (startIdx > 0) + { + get<2>(maxStack.front())--; // Decrement stack's front num shares. + windowMaxSum -= get<1>(maxStack.front()); + + // Front num out of window. + if (get<0>(maxStack.front()) < startIdx) + maxStack.pop_front(); + + get<2>(minStack.front())--; // Decrement stack's front num shares. + windowMinSum -= get<1>(minStack.front()); + + // Front num out of window. + if (get<0>(minStack.front()) < startIdx) + minStack.pop_front(); + } -``` + long long num = nums[endIdx]; + + long long maxShares = 1; // Base case. + windowMaxSum += num; + + while (!maxStack.empty() && get<1>(maxStack.back()) <= num) + { + int prevNum = get<1>(maxStack.back()); + long long prevShares = get<2>(maxStack.back()); + maxStack.pop_back(); + + maxShares += prevShares; // Max shares transition. + + // Reflect transition in max sum. + windowMaxSum += (num - prevNum) * prevShares; + } -#### Go + maxStack.push_back({endIdx, num, maxShares}); -```go + long long minShares = 1; // Base case. + windowMinSum += num; + while (!minStack.empty() && get<1>(minStack.back()) >= num) + { + int prevNum = get<1>(minStack.back()); + long long prevShares = get<2>(minStack.back()); + minStack.pop_back(); + + minShares += prevShares; // Min shares transition. + + // Reflect transition in min sum. + windowMinSum += (num - prevNum) * prevShares; + } + + minStack.push_back({endIdx, num, minShares}); + + totalMaxMinSum += windowMaxSum + windowMinSum; + } + + return totalMaxMinSum; + } +}; ``` #### JavaScript diff --git a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README_EN.md b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README_EN.md index aba507f4f5210..785dd6f08e119 100644 --- a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README_EN.md +++ b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/README_EN.md @@ -161,32 +161,184 @@ tags: -### Solution 1 +### Solution 1: Monotonic Deques for Maximums and Minimums + +The goal is to calculate total sum $S = \sum_i (\text{MaxSum}_i + \text{MinSum}_i)$, where: +1. $\text{MaxSum}_i$: sum of maximum values of all valid subarrays ending at index $i$. +2. $\text{MinSum}_i$: sum of minimum values of all valid subarrays ending at index $i$. + +We maintain two **Deques**, used as monotonic stacks with boundary control: `max_stack` and `min_stack`. + +These track maximum and minimum values for all subarrays ending at index $i$ with a length not exceeding $k$. + +Each element in deques is a triplet: `[index, value, count]`, +where `count` represents how many times this `value` acts as the max/min within current window. + +#### Step 1: Boundary Control +For each element $nums[i]$, check if window boundary $\max(0, i - k + 1)$ has advanced. +* If $i - k \ge 0$, it means the subarray $nums[i-k \dots i]$ would exceed length $k$ due to inclusion of $nums[i]$. +* Contribution of the subarray starting at $i-k$ must be removed. This contribution is located at the **front** of our deques. +* We decrement the `count` of deques' front. If the front element's index falls out of window range, we `popleft()` to maintain efficiency. + +#### Step 2: Monotonicity +Taking `max_stack` as an example: + +* While `max_stack` is not empty and `max_stack` top value $\leq nums[i]$ (1): + * Current $nums[i]$ will replace this top element as the new maximum for all subarrays this top element previously "served." + * We take over the `prev_shares` (which is the count) from this popped element. + * Net increase to `subarrays_max_sum` is calculated as $(nums[i] - prev\_num) \times prev\_shares$. + +* After while-loop, we add $nums[i]$'s own contribution, as a single-element subarray, to `subarrays_max_sum`, +and push $nums[i]$ onto `max_stack` with its cumulative `count` and index. + +Logic for `min_stack` processing is similar, but inequality (1) must change into `min_stack` top value $\geq nums[i]$. + +#### Step 3: Accumulation +At the end of each iteration $i$, we add current `subarrays_max_sum` and `subarrays_min_sum` to global total `subarrays_max_min_sum`. + +### Complexity Analysis +* **Time Complexity:** $O(n)$, where $n$ is length of $nums$. Each element is pushed and popped at most 4 times in total among two deques. +* **Space Complexity:** $O(n)$ to store deques and state variables. #### Python3 ```python +class Solution: + def minMaxSubarraySum(self, nums: list[int], k: int) -> int: + subarrays_max_min_sum = 0 -``` + max_stack: deque[list[int]] = deque([]) # Format: [idx, num, shares]. + subarrays_max_sum = 0 + + min_stack: deque[list[int]] = deque([]) # Format: [idx, num, shares]. + subarrays_min_sum = 0 + + for end_idx, num in enumerate(nums): + start_idx = max(0, end_idx - k + 1) + + # Window start idx slides by 1: must update stacks' info. + if start_idx > 0: + max_stack[0][2] -= 1 # Decrement stack's front num shares. + subarrays_max_sum -= max_stack[0][1] + + if max_stack[0][0] < start_idx: # Front num out of window. + max_stack.popleft() + + min_stack[0][2] -= 1 # Decrement stack's front num shares. + subarrays_min_sum -= min_stack[0][1] + + if min_stack[0][0] < start_idx: # Front num out of window. + min_stack.popleft() + + max_shares = 1 # Base case. + subarrays_max_sum += num + + while max_stack and max_stack[-1][1] <= num: + _, prev_num, prev_shares = max_stack.pop() + + max_shares += prev_shares # Max shares transition. + + # Reflect transition in max sum. + subarrays_max_sum += (num - prev_num) * prev_shares -#### Java + max_stack.append([end_idx, num, max_shares]) -```java + min_shares = 1 # Base case. + subarrays_min_sum += num + while min_stack and min_stack[-1][1] >= num: + _, prev_num, prev_shares = min_stack.pop() + + min_shares += prev_shares # Min shares transition. + + # Reflect transition in min sum. + subarrays_min_sum += (num - prev_num) * prev_shares + + min_stack.append([end_idx, num, min_shares]) + + subarrays_max_min_sum += subarrays_max_sum + subarrays_min_sum + + return subarrays_max_min_sum ``` #### C++ ```cpp +class Solution { +public: + long long minMaxSubarraySum(vector& nums, int k) { + long long totalMaxMinSum = 0; + long long windowMaxSum = 0, windowMinSum = 0; + + // Format: {idx, num, shares}. Use long long to prevent overflow. + deque> maxStack, minStack; + + for (int endIdx = 0; endIdx < nums.size(); endIdx++) + { + int startIdx = max(0, endIdx - k + 1); + + // Window start idx slides by 1: must update stacks' info. + if (startIdx > 0) + { + get<2>(maxStack.front())--; // Decrement stack's front num shares. + windowMaxSum -= get<1>(maxStack.front()); + + // Front num out of window. + if (get<0>(maxStack.front()) < startIdx) + maxStack.pop_front(); + + get<2>(minStack.front())--; // Decrement stack's front num shares. + windowMinSum -= get<1>(minStack.front()); + + // Front num out of window. + if (get<0>(minStack.front()) < startIdx) + minStack.pop_front(); + } -``` + long long num = nums[endIdx]; + + long long maxShares = 1; // Base case. + windowMaxSum += num; + + while (!maxStack.empty() && get<1>(maxStack.back()) <= num) + { + int prevNum = get<1>(maxStack.back()); + long long prevShares = get<2>(maxStack.back()); + maxStack.pop_back(); + + maxShares += prevShares; // Max shares transition. -#### Go + // Reflect transition in max sum. + windowMaxSum += (num - prevNum) * prevShares; + } + + maxStack.push_back({endIdx, num, maxShares}); + + long long minShares = 1; // Base case. + windowMinSum += num; + + while (!minStack.empty() && get<1>(minStack.back()) >= num) + { + int prevNum = get<1>(minStack.back()); + long long prevShares = get<2>(minStack.back()); + minStack.pop_back(); + + minShares += prevShares; // Min shares transition. -```go + // Reflect transition in min sum. + windowMinSum += (num - prevNum) * prevShares; + } + + minStack.push_back({endIdx, num, minShares}); + totalMaxMinSum += windowMaxSum + windowMinSum; + } + + return totalMaxMinSum; + } +}; ``` #### JavaScript diff --git a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp new file mode 100644 index 0000000000000..b43e9786cbd5c --- /dev/null +++ b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp @@ -0,0 +1,73 @@ +class Solution { +public: + long long minMaxSubarraySum(vector& nums, int k) { + long long totalMaxMinSum = 0; + long long windowMaxSum = 0, windowMinSum = 0; + + // Format: {idx, num, shares}. Use long long to prevent overflow. + deque> maxStack, minStack; + + for (int endIdx = 0; endIdx < nums.size(); endIdx++) + { + int startIdx = max(0, endIdx - k + 1); + + // Window start idx slides by 1: must update stacks' info. + if (startIdx > 0) + { + get<2>(maxStack.front())--; // Decrement stack's front num shares. + windowMaxSum -= get<1>(maxStack.front()); + + // Front num out of window. + if (get<0>(maxStack.front()) < startIdx) + maxStack.pop_front(); + + get<2>(minStack.front())--; // Decrement stack's front num shares. + windowMinSum -= get<1>(minStack.front()); + + // Front num out of window. + if (get<0>(minStack.front()) < startIdx) + minStack.pop_front(); + } + + long long num = nums[endIdx]; + + long long maxShares = 1; // Base case. + windowMaxSum += num; + + while (!maxStack.empty() && get<1>(maxStack.back()) <= num) + { + int prevNum = get<1>(maxStack.back()); + long long prevShares = get<2>(maxStack.back()); + maxStack.pop_back(); + + maxShares += prevShares; // Max shares transition. + + // Reflect transition in max sum. + windowMaxSum += (num - prevNum) * prevShares; + } + + maxStack.push_back({endIdx, num, maxShares}); + + long long minShares = 1; // Base case. + windowMinSum += num; + + while (!minStack.empty() && get<1>(minStack.back()) >= num) + { + int prevNum = get<1>(minStack.back()); + long long prevShares = get<2>(minStack.back()); + minStack.pop_back(); + + minShares += prevShares; // Min shares transition. + + // Reflect transition in min sum. + windowMinSum += (num - prevNum) * prevShares; + } + + minStack.push_back({endIdx, num, minShares}); + + totalMaxMinSum += windowMaxSum + windowMinSum; + } + + return totalMaxMinSum; + } +}; \ No newline at end of file diff --git a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py new file mode 100644 index 0000000000000..5b334e9dcb981 --- /dev/null +++ b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py @@ -0,0 +1,57 @@ + +class Solution: + def minMaxSubarraySum(self, nums: list[int], k: int) -> int: + subarrays_max_min_sum = 0 + + max_stack: deque[list[int]] = deque([]) # Format: [idx, num, shares]. + subarrays_max_sum = 0 + + min_stack: deque[list[int]] = deque([]) # Format: [idx, num, shares]. + subarrays_min_sum = 0 + + for end_idx, num in enumerate(nums): + start_idx = max(0, end_idx - k + 1) + + # Window start idx slides by 1: must update stacks' info. + if start_idx > 0: + max_stack[0][2] -= 1 # Decrement stack's front num shares. + subarrays_max_sum -= max_stack[0][1] + + if max_stack[0][0] < start_idx: # Front num out of window. + max_stack.popleft() + + min_stack[0][2] -= 1 # Decrement stack's front num shares. + subarrays_min_sum -= min_stack[0][1] + + if min_stack[0][0] < start_idx: # Front num out of window. + min_stack.popleft() + + max_shares = 1 # Base case. + subarrays_max_sum += num + + while max_stack and max_stack[-1][1] <= num: + _, prev_num, prev_shares = max_stack.pop() + + max_shares += prev_shares # Max shares transition. + + # Reflect transition in max sum. + subarrays_max_sum += (num - prev_num) * prev_shares + + max_stack.append([end_idx, num, max_shares]) + + min_shares = 1 # Base case. + subarrays_min_sum += num + + while min_stack and min_stack[-1][1] >= num: + _, prev_num, prev_shares = min_stack.pop() + + min_shares += prev_shares # Min shares transition. + + # Reflect transition in min sum. + subarrays_min_sum += (num - prev_num) * prev_shares + + min_stack.append([end_idx, num, min_shares]) + + subarrays_max_min_sum += subarrays_max_sum + subarrays_min_sum + + return subarrays_max_min_sum From e627b6db6a9e8fc836c53aef9320d3506a664534 Mon Sep 17 00:00:00 2001 From: Jack Yao <105488074+StarsExpress@users.noreply.github.com> Date: Tue, 12 May 2026 10:19:16 -0400 Subject: [PATCH 2/2] Style: reformatted code to fit .clang-format. --- .../Solution.cpp | 12 ++++-------- .../Solution.py | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp index b43e9786cbd5c..12332337f7c48 100644 --- a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp +++ b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.cpp @@ -7,13 +7,11 @@ class Solution { // Format: {idx, num, shares}. Use long long to prevent overflow. deque> maxStack, minStack; - for (int endIdx = 0; endIdx < nums.size(); endIdx++) - { + for (int endIdx = 0; endIdx < nums.size(); endIdx++) { int startIdx = max(0, endIdx - k + 1); // Window start idx slides by 1: must update stacks' info. - if (startIdx > 0) - { + if (startIdx > 0) { get<2>(maxStack.front())--; // Decrement stack's front num shares. windowMaxSum -= get<1>(maxStack.front()); @@ -34,8 +32,7 @@ class Solution { long long maxShares = 1; // Base case. windowMaxSum += num; - while (!maxStack.empty() && get<1>(maxStack.back()) <= num) - { + while (!maxStack.empty() && get<1>(maxStack.back()) <= num) { int prevNum = get<1>(maxStack.back()); long long prevShares = get<2>(maxStack.back()); maxStack.pop_back(); @@ -51,8 +48,7 @@ class Solution { long long minShares = 1; // Base case. windowMinSum += num; - while (!minStack.empty() && get<1>(minStack.back()) >= num) - { + while (!minStack.empty() && get<1>(minStack.back()) >= num) { int prevNum = get<1>(minStack.back()); long long prevShares = get<2>(minStack.back()); minStack.pop_back(); diff --git a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py index 5b334e9dcb981..342a1ba9bf2ee 100644 --- a/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py +++ b/solution/3400-3499/3430.Maximum and Minimum Sums of at Most Size K Subarrays/Solution.py @@ -1,4 +1,3 @@ - class Solution: def minMaxSubarraySum(self, nums: list[int], k: int) -> int: subarrays_max_min_sum = 0