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 possible right endpoints, adding one more rightward element each time. This takes .
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 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 .
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 .
C++ Solution
1int maxSumSubarray(vector<int> &array, int k) {
2 int ans = INT_MIN;
3 deque<pair<int, int>> q;
4 q.emplace_back(0, -1);
5 for (int i = 0, prefixSum = 0; i < array.size(); i++) {
6 prefixSum += array[i];
7 // Remove left endpoints that are more than distance k away
8 while (q.size() and q.front().second < i-k) {
9 q.pop_front();
10 }
11 ans = max(ans, prefixSum - q.front().first);
12 // Ensure monotonicity of the deque before appending (prefixSum, i)
13 while (q.size() and q.back().first >= prefixSum) {
14 q.pop_back();
15 }
16 q.emplace_back(prefixSum, i);
17 }
18 return ans;
19}
Java Solution
1static class Pair {
2 int first, second;
3 Pair(int first, int second) { this.first = first; this.second = second; }
4}
5
6static int maxSumSubarray(int[] array, int k) {
7 int ans = Integer.MIN_VALUE;
8 Deque<Pair> q = new ArrayDeque<>();
9 q.addLast(new Pair(0, -1));
10 for (int i = 0, prefixSum = 0; i < array.length; i++) {
11 prefixSum += array[i];
12 // Remove left endpoints that are more than distance k away
13 while (!q.isEmpty() && q.peekFirst().second < i-k) {
14 q.removeFirst();
15 }
16 ans = Math.max(ans, prefixSum - q.peekFirst().first);
17 // Ensure monotonicity of the deque before appending (prefixSum, i)
18 while (!q.isEmpty() && q.peekLast().first >= prefixSum) {
19 q.removeLast();
20 }
21 q.addLast(new Pair(prefixSum, i));
22 }
23 return ans;
24}
Python Solution
1def maxSumSubarray(array, k): 2 ans = -10**9 3 q = deque([(0, -1)]) 4 prefixSum = 0 5 for i in range(len(array)): 6 prefixSum += array[i] 7 # Remove left endpoints that are more than distance k away 8 while len(q) and q[0][1] < i-k: 9 q.popleft() 10 ans = max(ans, prefixSum - q[0][0]) 11 # Ensure monotonicity of the deque before appending (prefixSum, i) 12 while len(q) and q[-1][0] >= prefixSum: 13 q.pop() 14 q.append((prefixSum, i)) 15 return ans
Which of the following is the prefix sum of array [1, 2, 3, 4, 5]
?
Which two pointer techniques do you use to check if a string is a palindrome?
Solution Implementation
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
Top Patterns to Conquer the Technical Coding Interview Should the written word bore you fear not A delightful video alternative awaits iframe width 560 height 315 src https www youtube com embed LW8Io6IPYHw title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture
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
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Got a question? Ask the Teaching Assistant anything you don't understand.
Still not clear? Ask in the Forum, Discord or Submit the part you don't understand to our editors.