3381. Maximum Subarray Sum With Length Divisible by K
Problem Description
You are given an array of integers nums
and an integer k
. You need to find the maximum sum of a subarray of nums
, such that the size of this subarray is divisible by k
.
Intuition
The problem requires us to not only find a subarray with a maximum sum but also ensure the subarray's length is divisible by k
. To solve this, we use a prefix sum approach combined with modulus operation to maintain subarrays of specific lengths.
-
Prefix Sum and Modulus: We use prefix sums to efficiently compute sums of subarrays. By maintaining an array
f
wheref[i]
represents the minimum prefix sum seen so far whose index has a modulusi % k
, we can check for subarrays ending at any position with the required length divisible byk
. -
Iterative Calculation: As we iterate through
nums
, we updates
, which is the running prefix sum. For each element, we determine the potential maximum sum of a subarray ending at that element using the formulas - f[i % k]
. This ensures that we consider only those subarrays whose length is divisible byk
. -
Update of Minimums: After evaluating the potential maximum for the current element, we update
f[i % k]
to ensure it holds the smallest prefix sum for its corresponding modulus, helping maintain the condition for subarray lengths.
This efficient strategy allows us to determine the required subarray using a single pass through nums
, operating in linear time and space relative to the number of elements and the divisor k
.
Learn more about Prefix Sum patterns.
Solution Approach
The solution employs a combination of prefix sums and modulus operations using an array to keep track of subarrays of certain lengths. Here's a step-by-step breakdown of the approach:
-
Initialization:
- We start by initializing an array
f
of sizek
with all elements set to infinity (inf
), except forf[k-1]
which is set to0
. This arrayf
is used to track the minimum prefix sum recorded for each modulus when the index is divided byk
. - We also initialize
ans
with a very negative value (-inf
) to ensure any valid subarray sum will replace it. s
is initialized to0
to accumulate the running sum of the elements innums
.
- We start by initializing an array
-
Iterate Through
nums
:- For each element
x
innums
, updates
to include the current element, sos += x
. - Compute the current potential maximum subarray sum as
s - f[i % k]
and updateans
if this value is greater.
- For each element
-
Update Minimum Prefix Sums:
- For maintaining the validity of the modulus condition, update
f[i % k]
asmin(f[i % k], s)
to ensure it retains the smallest prefix sum with the same modulus. - By the end of the iteration,
ans
accumulates the maximum sum of a subarray whose length is divisible byk
.
- For maintaining the validity of the modulus condition, update
The key operation here involves efficiently calculating the necessary subarray sums using a constant-time update on an array of size k
.
Here's the core code integrated into this approach:
class Solution:
def maxSubarraySum(self, nums: List[int], k: int) -> int:
f = [inf] * k
ans = -inf
s = f[-1] = 0
for i, x in enumerate(nums):
s += x
ans = max(ans, s - f[i % k])
f[i % k] = min(f[i % k], s)
return ans
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a small example to illustrate the process:
Example: Consider nums = [3, -1, 2, 1, -4, 2, 6]
and k = 3
. We need to find the maximum sum of a subarray whose length is divisible by k
.
Step 1: Initialization
- Initialize
f = [inf, inf, 0]
where k = 3 leads to this size, withf[2]
set to0
based on initial setup. - Set
ans = -inf
to ensure a smaller value is replaced. - Start with a running prefix sum
s = 0
.
Step 2: Iteration Over nums
-
Iteration 1:
x = 3
- Update
s = 0 + 3 = 3
- Calculate potential maximum:
s - f[0 % 3] = 3 - f[0] = 3 - inf = -inf
(not useful) - Since no update to
ans
, consider minimum update:f[0] = min(inf, 3) = 3
- Update
-
Iteration 2:
x = -1
- Update
s = 3 - 1 = 2
- Calculate potential maximum:
s - f[1 % 3] = 2 - f[1] = 2 - inf = -inf
- Update
f[1] = min(inf, 2) = 2
- Update
-
Iteration 3:
x = 2
- Update
s = 2 + 2 = 4
- Calculate potential maximum:
s - f[2 % 3] = 4 - f[2] = 4 - 0 = 4
- Update
ans = max(-inf, 4) = 4
- Update
f[2] = min(0, 4) = 0
- Update
-
Iteration 4:
x = 1
- Update
s = 4 + 1 = 5
- Calculate potential maximum:
s - f[0 % 3] = 5 - f[0] = 5 - 3 = 2
ans
remains4
. Update minimum:f[0] = min(3, 5) = 3
- Update
-
Iteration 5:
x = -4
- Update
s = 5 - 4 = 1
- Calculate potential maximum:
s - f[1 % 3] = 1 - f[1] = 1 - 2 = -1
ans
remains4
. Update:f[1] = min(2, 1) = 1
- Update
-
Iteration 6:
x = 2
- Update
s = 1 + 2 = 3
- Calculate potential maximum:
s - f[2 % 3] = 3 - f[2] = 3 - 0 = 3
ans
remains4
. Update:f[2] = min(0, 3) = 0
- Update
-
Iteration 7:
x = 6
- Update
s = 3 + 6 = 9
- Calculate potential maximum:
s - f[0 % 3] = 9 - f[0] = 9 - 3 = 6
- Update
ans = max(4, 6) = 6
- Update
Final Result: The maximum sum of a subarray with length divisible by 3 is 6
.
Solution Implementation
1from typing import List
2from math import inf
3
4class Solution:
5 def maxSubarraySum(self, nums: List[int], k: int) -> int:
6 # Initialize the prefix sum remainders to a large number (infinity)
7 prefix_sum_remainders = [inf] * k
8 # Initialize the maximum subarray sum to a very small number
9 max_sum = -inf
10 # Initialize the current prefix sum; starting with zero
11 current_sum = prefix_sum_remainders[-1] = 0
12
13 for i, num in enumerate(nums):
14 # Update the current prefix sum by adding the current number
15 current_sum += num
16 # Update the maximum sum by checking the current prefix sum
17 # minus the smallest prefix sum remainder with the same mod k
18 max_sum = max(max_sum, current_sum - prefix_sum_remainders[i % k])
19 # Update the prefix sum remainder with the minimum value found
20 prefix_sum_remainders[i % k] = min(prefix_sum_remainders[i % k], current_sum)
21
22 return max_sum
23
1import java.util.Arrays;
2
3class Solution {
4 public long maxSubarraySum(int[] nums, int k) {
5 // Array to store the minimum prefix sum for each of the k mod classes
6 long[] minPrefixSum = new long[k];
7 // Define a large sentinel value for initialization
8 final long INF = 1L << 62;
9 // Initialize all elements to INF as the worst-case scenario
10 Arrays.fill(minPrefixSum, INF);
11 // Start with the last index for the optimal subarray starting point
12 minPrefixSum[k - 1] = 0;
13
14 long currentSum = 0; // To track the current prefix sum
15 long maxSum = -INF; // To track the maximum subarray sum found
16
17 // Iterate over the array to compute the maximum subarray sum
18 for (int i = 0; i < nums.length; ++i) {
19 currentSum += nums[i];
20 // Update maxSum by considering the current prefix sum minus
21 // the minimum prefix sum of the same mod class
22 maxSum = Math.max(maxSum, currentSum - minPrefixSum[i % k]);
23 // Update the minimum prefix sum for the current mod class
24 minPrefixSum[i % k] = Math.min(minPrefixSum[i % k], currentSum);
25 }
26
27 return maxSum;
28 }
29}
30
1#include <vector>
2#include <algorithm>
3#include <limits>
4
5class Solution {
6public:
7 long long maxSubarraySum(std::vector<int>& nums, int k) {
8 using ll = long long;
9 // Define a very large value representing positive infinity
10 ll inf = std::numeric_limits<ll>::max();
11
12 // Initialize a vector with 'k' elements, each set to infinity
13 std::vector<ll> f(k, inf);
14
15 // Variable to store the maximum sum found
16 ll ans = -inf;
17 // Variable to store the cumulative sum
18 ll s = 0;
19
20 // Set the last element of the vector 'f' to zero
21 f[k - 1] = 0;
22
23 // Iterate over the elements of the input vector
24 for (int i = 0; i < nums.size(); ++i) {
25 // Update cumulative sum with the current element
26 s += nums[i];
27 // Update the maximum subarray sum
28 ans = std::max(ans, s - f[i % k]);
29 // Update the min cumulative sum at the index 'i % k'
30 f[i % k] = std::min(f[i % k], s);
31 }
32
33 // Return the maximum subarray sum found
34 return ans;
35 }
36};
37
1// Function to find the maximum subarray sum such that no two elements are within 'k' distance.
2function maxSubarraySum(nums: number[], k: number): number {
3 // Array to store minimum prefix sums modulo k
4 const prefixMin: number[] = Array(k).fill(Infinity);
5 // Initialize to zero to handle the case of subarrays starting at the beginning
6 prefixMin[k - 1] = 0;
7 // Variable to store maximum subarray sum found
8 let maxSum = -Infinity;
9 // Variable to keep track of current prefix sum
10 let currentSum = 0;
11
12 // Iterate through the list of numbers
13 for (let i = 0; i < nums.length; ++i) {
14 // Update current prefix sum
15 currentSum += nums[i];
16 // Update maximum subarray sum using the prefix sum and the minimum sum in the prefix array
17 maxSum = Math.max(maxSum, currentSum - prefixMin[i % k]);
18 // Update the minimum prefix sum for this modulo group
19 prefixMin[i % k] = Math.min(prefixMin[i % k], currentSum);
20 }
21
22 // Return the maximum subarray sum found
23 return maxSum;
24}
25
Time and Space Complexity
The code iterates over the nums
list once to compute the maximum subarray sum mod k
. Therefore, the time complexity is O(n)
, where n
is the length of the nums
list. Each operation within the loop (addition, subtraction, modulo, max/min operations) happens in constant time.
For space complexity, it maintains an array f
of size k
to store the minimum prefix sums mod k
. Thus, the space complexity is O(k)
.
Learn more about how to find time and space complexity quickly.
Which data structure is used to implement priority queue?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!