Akuna OA

Given an array a of length n, find the maximum possible sum from a subarray of length at most k.

Constraints: 1 <= k <= n <= 1000

Example 1:

Input: a = [2, -3, 5, 3, -1, 2], k = 4

Output: 9

Explanation: The maximum sum subarray is [5, 3, -1, 2].

Example 2:

Input: a = [2, -3, 5, 3, -1, 2], k = 3

Output: 8

Explanation: The maximum sum subarray is [5, 3]. We can't take [5, 3, -1, 2] as we did in example 1 because it would exceed the maximum length.


Solution

Naive Solution

Try all starting left endpoints and for each one, try all the O(k)\mathcal{O}(k) possible right endpoints, adding one more rightward element each time. This takes O(nk)\mathcal{O}(nk).

Model Solution

Let psa[i] = a[0] + a[1] + ... + a[i]. In other words, psa is a prefix sum array of a.

Consider the subarray from l to r inclusive. a[l] + a[l+1] + ... + a[r-1] + a[r] = psa[r] - psa[l-1].

For every possible r, find the l that maximizes psa[r] - psa[l-1] where r+k-1 <= l <= r (we still need the length of the subarray to be <=k ). This is still O(nk)\mathcal{O}(nk) unless we optimize the search for l.

Let's change what l represents. Instead of l and r characterizing an inclusive subarray, we now exclude the left endpoint, so it has a sum of a[l+1] + a[l+2] + ... + a[r-1] + a[r] = psa[r] - psa[l]. We change this notation to make indexing simpler—we get rid of the -1 from psa[l-1].

We can create a monotonic deque to store (psa[i], i) pairs in increasing order of psa[i]. We loop r from 0 to n-1. For each r, we want the index l (l < r) with the minimum psa[l], which is the one at the front of the deque. But if l < r-k, our subarray would be too long. To solve this problem, we remove all l less than r-k from the deque. Now we can let l be the first element of the deque and set ans := max(ans, psa[r]-psa[l]).

The current r may be a future left endpoint. To consider this, insert (psa[r], r) into the deque. To ensure that the deque remains monotonically increasing, we repeatedly pop the back element i from the deque until psa[i] < psa[r] (review the Monotonic Stack/Deque Intro article if you're confused). Now we increment r by 1 and repeat until we loop through the entire array.

Time Complexity

We loop over each element once, insert each element into the deque once, and pop each element from the deque at most once. The time complexity is O(n)\mathcal{O}(n).

Recall that n <= 1000, but our solution can work for much larger n: around the order of 10^8 or 10^9. This'll sure impress your interviewer. 😉

Space Complexity

The deque contains up to n elements. The space complexity is O(n)\mathcal{O}(n).

C++ Solution

int maxSumSubarray(vector<int> &array, int k) {
    int ans = INT_MIN;
    deque<pair<int, int>> q;
    q.emplace_back(0, -1);
    for (int i = 0, prefixSum = 0; i < array.size(); i++) {
        prefixSum += array[i];
        // Remove left endpoints that are more than distance k away
        while (q.size() and q.front().second < i-k) {
            q.pop_front();
        }
        ans = max(ans, prefixSum - q.front().first);
        // Ensure monotonicity of the deque before appending (prefixSum, i)
        while (q.size() and q.back().first >= prefixSum) {
            q.pop_back();
        }
        q.emplace_back(prefixSum, i);
    }
    return ans;
}

Java Solution

static class Pair {
    int first, second;
    Pair(int first, int second) { this.first = first; this.second = second; }
}

static int maxSumSubarray(int[] array, int k) {
    int ans = Integer.MIN_VALUE;
    Deque<Pair> q = new ArrayDeque<>();
    q.addLast(new Pair(0, -1));
    for (int i = 0, prefixSum = 0; i < array.length; i++) {
        prefixSum += array[i];
        // Remove left endpoints that are more than distance k away
        while (!q.isEmpty() && q.peekFirst().second < i-k) {
            q.removeFirst();
        }
        ans = Math.max(ans, prefixSum - q.peekFirst().first);
        // Ensure monotonicity of the deque before appending (prefixSum, i)
        while (!q.isEmpty() && q.peekLast().first >= prefixSum) {
            q.removeLast();
        }
        q.addLast(new Pair(prefixSum, i));
    }
    return ans;
}

Python Solution

def maxSumSubarray(array, k):
    ans = -10**9
    q = deque([(0, -1)])
    prefixSum = 0
    for i in range(len(array)):
        prefixSum += array[i]
        # Remove left endpoints that are more than distance k away
        while len(q) and q[0][1] < i-k:
            q.popleft()
        ans = max(ans, prefixSum - q[0][0])
        # Ensure monotonicity of the deque before appending (prefixSum, i)
        while len(q) and q[-1][0] >= prefixSum:
            q.pop()
        q.append((prefixSum, i))
    return ans

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator
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 recursion?


Recommended Readings

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