Facebook Pixel

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.

  1. Prefix Sum and Modulus: We use prefix sums to efficiently compute sums of subarrays. By maintaining an array f where f[i] represents the minimum prefix sum seen so far whose index has a modulus i % k, we can check for subarrays ending at any position with the required length divisible by k.

  2. Iterative Calculation: As we iterate through nums, we update s, which is the running prefix sum. For each element, we determine the potential maximum sum of a subarray ending at that element using the formula s - f[i % k]. This ensures that we consider only those subarrays whose length is divisible by k.

  3. 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:

  1. Initialization:

    • We start by initializing an array f of size k with all elements set to infinity (inf), except for f[k-1] which is set to 0. This array f is used to track the minimum prefix sum recorded for each modulus when the index is divided by k.
    • We also initialize ans with a very negative value (-inf) to ensure any valid subarray sum will replace it.
    • s is initialized to 0 to accumulate the running sum of the elements in nums.
  2. Iterate Through nums:

    • For each element x in nums, update s to include the current element, so s += x.
    • Compute the current potential maximum subarray sum as s - f[i % k] and update ans if this value is greater.
  3. Update Minimum Prefix Sums:

    • For maintaining the validity of the modulus condition, update f[i % k] as min(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 by k.

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 Evaluator

Example 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, with f[2] set to 0 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
  • 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
  • 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
  • Iteration 4: x = 1

    • Update s = 4 + 1 = 5
    • Calculate potential maximum: s - f[0 % 3] = 5 - f[0] = 5 - 3 = 2
    • ans remains 4. Update minimum: f[0] = min(3, 5) = 3
  • Iteration 5: x = -4

    • Update s = 5 - 4 = 1
    • Calculate potential maximum: s - f[1 % 3] = 1 - f[1] = 1 - 2 = -1
    • ans remains 4. Update: f[1] = min(2, 1) = 1
  • Iteration 6: x = 2

    • Update s = 1 + 2 = 3
    • Calculate potential maximum: s - f[2 % 3] = 3 - f[2] = 3 - 0 = 3
    • ans remains 4. Update: f[2] = min(0, 3) = 0
  • 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

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.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Which data structure is used to implement priority queue?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More