Longest Subarray with Sum K
Given the integers nums and a target k, return the length of the longest subarray whose elements total exactly k. A subarray is a stretch of consecutive elements, at least one long. Some values may be zero or below zero. If no subarray adds up to k, return 0.
Function
- numsinteger-array
- the integers to search, negatives and zeros included
- kinteger
- the exact sum the subarray must have
- Returnsinteger
- the length of the longest subarray that adds up to k, or 0 when none does
Constraints
1 ≤ nums.length ≤ 2 × 104-104 ≤ nums[i] ≤ 104-109 ≤ k ≤ 109- Every prefix sum stays between
-2 × 108and2 × 108, so 32-bit integers are enough. - Return
0when no subarray adds up tok.
Examples
- Input
- nums = [2, -1, 3, 1, -4, 2, 3]k = 5
- Output
- 5
- Explanation
- Three runs add up to 5:
[2, 3]at the end,[2, -1, 3, 1]at the start, and[3, 1, -4, 2, 3]at indices 2 to 6. The last is the longest, with 5 elements.
- Input
- nums = [4, 1, 1, 2, -1, 1]k = 3
- Output
- 4
- Explanation
[1, 1, 2, -1]at indices 1 to 4 and[1, 2, -1, 1]at indices 2 to 5 both add up to 3 with 4 elements. You return a length, so the tie does not matter.
- Input
- nums = [-3, 6, -3]k = 1
- Output
- 0
- Explanation
- The six subarray sums are -3, 6, -3, 3, 3 and 0. None of them is 1, so the answer is 0.
Follow-up
How would you find the longest subarray whose sum is at most k, with negative values still allowed?
Hints
Open them one at a time. Each one gives away a little more.
With 20,000 numbers, the brute force adds up about 2 × 10^8 start and end pairs. Negative values also break a sliding window: a run that is over
kcan come back down to it. Which running total would let you get any subarray's sum with one subtraction?With running prefix sums, the elements after position
iup to positionjadd up toprefix[j] - prefix[i]. A subarray ending atjhas sumkwhen an earlier prefix equalsprefix[j] - k. If several earlier prefixes qualify, which one gives the longest subarray?Record, for each running total, the first index where it appeared, starting with total 0 at index -1. At each index, look up
prefix - kand compare the distance with your best length. Add the current prefix to the map only when it is not there yet.
Solution
Running totals turn any subarray's sum into one subtraction: the elements after index s up to index i add up to prefix[i] - prefix[s]. Remember the first index where each total appears, and one pass finds the longest match ending at every position.
Grow every subarray from each start
Correct, but does not finish on the largest tests
Intuition
From each start, extend to the right and add each element to a running total. Whenever the total equals k, record the length. Every subarray is one pair of start and end, so none is missed. Keep extending after the total passes k: from index 2 in the first example the totals are 3, 4, 0, 2, 5, and the -4 is why the last one reaches 5.
There are about n²/2 pairs: 2 × 10^8 additions for n = 2 × 10^4.
Algorithm
- Set
bestto 0. - For each
start, settotalto 0. - Move
endfromstartto the last index and addnums[end]tototal. - When
totalequalsk, setbestto the larger ofbestandend - start + 1. - Return
best.
def longestSubarrayWithSumK(nums, k):
n = len(nums)
best = 0
for start in range(n):
total = 0
for end in range(start, n):
total += nums[end]
# A match may come after the total has passed k, so never stop early
if total == k and end - start + 1 > best:
best = end - start + 1
return bestPrefix sums with the first index of each sum
Intuition
A run ending at index i sums to k exactly when some earlier total equals prefix - k. If several earlier totals qualify, the first one starts the longest run, so the map stores each total only the first time it appears. A later copy could only start a shorter run. Seed the map with total 0 at index -1, so a match that starts at index 0 gets length i + 1.
In the first example the totals are 2, 1, 4, 5, 1, 3, 6. At index 6, 6 - 5 = 1 first appeared at index 1, which gives length 5. Total 1 comes back at index 4, and keeping index 1 is what finds that run.
One lookup per element gives O(n) time, and the map holds at most n + 1 totals.
Algorithm
- Create a map
firstholding total 0 at index -1, and setprefixandbestto 0. - For each index
i, addnums[i]toprefix. - If
prefix - kis in the map at indexstart, setbestto the larger ofbestandi - start. - If
prefixis not in the map yet, store it with indexi. - Return
best.
def longestSubarrayWithSumK(nums, k):
# first[p] is the index where the prefix sum p first appears; the empty prefix sits at -1
first = {0: -1}
prefix = 0
best = 0
for i, num in enumerate(nums):
prefix += num
# The earliest prefix equal to prefix - k starts the longest match that ends at i
start = first.get(prefix - k)
if start is not None and i - start > best:
best = i - start
# Keep the first index only: a later one could only start a shorter subarray
if prefix not in first:
first[prefix] = i
return best
Pitfalls and edge cases
- Overwriting the stored index each time a total repeats keeps the latest start and finds a shorter match. The first example returns 4 instead of 5, because total 1 moves from index 1 to index 4.
- A window that shrinks while its sum is above
kalso returns 4 on the first example. It ends holding[-1, 3, 1, -4, 2, 3]with sum 4 and never drops the -1. - Leaving out total 0 at index -1 misses every match that starts at index 0:
[5, 0, 0, 0]withk = 5returns 0 instead of 4. - The stored index is the position before the subarray, so the length is
i - start. Adding 1 counts one element too many.
Frequently asked questions3
What is the time complexity of Longest Subarray with Sum K?
O(n) time and O(n) extra space with running totals and a map of first indexes: one lookup and at most one insert per element. Growing every subarray from each start costs O(n²) time and O(1) space.
Why does a sliding window fail when the array has negative numbers?
A sliding window assumes that adding an element raises the sum and removing one lowers it. With negative values, a window whose sum is too big can still reach k once a negative element arrives, and one whose sum is too small may need to drop a negative element on its left. The window cannot tell which move is right.
Can Longest Subarray with Sum K be solved in O(1) extra space?
Only when no value is negative. Then the sum never drops as the window grows, and two pointers find the longest window with sum k in O(n) time and O(1) space. With negative values you need the map of totals, or the O(n²) scan.
Similar problems
Problems that use the same ideas. Solving two or three of them is what makes a pattern stick.
def longestSubarrayWithSumK(nums, k):
# Write code hereTest #1
Test #2
Test #3
Arguments
nums = [2, -1, 3, 1, -4, 2, 3] k = 5
Expected Output
5