Given an integer array nums
and an integer k
, find the length of the longest subarray that sums to exactly k
. The subarray must be contiguous (elements must be consecutive in the array), and you cannot reuse elements from non-overlapping positions. If there is no such subarray, return 0
.
nums
(an array of integers), k
(an integer target sum)k
At first glance, you might think to check every possible subarray, calculate its sum, and track the maximum length if the sum matches k
. However, this brute-force approach would require checking every start and end index pair, resulting in a time complexity of O(n2), which is inefficient for large arrays.
To optimize, we can use the concept of prefix sums. The idea is that if the sum of elements from index 0 to i
is sum_i
, and the sum from 0 to j
is sum_j
, then the sum of the subarray from i+1
to j
is sum_j - sum_i
. If we can find two indices where this difference equals k
, we've found a valid subarray.
To make this efficient, we use a hash map to record the earliest index where each prefix sum occurs. This allows us to check in constant time whether a subarray ending at the current index sums to k
.
prefix_sum_indices
) to store the first occurrence of each prefix sum. Also initialize current_sum
to 0 and max_length
to 0.
current_sum
by adding the current number.
current_sum
is exactly k
, update max_length
to the current index + 1 (since the subarray starts from the beginning).
current_sum - k
exists in the hash map, it means there is a previous prefix sum such that the subarray between that index + 1 and the current index sums to k
. Calculate the length and update max_length
if it's longer.
current_sum
is not already in the hash map, store it with the current index. (We only store the first occurrence to maximize the subarray length.)
max_length
.
Using a hash map for prefix sums ensures O(1) lookups, making the solution efficient.
Let's consider nums = [1, -1, 5, -2, 3]
and k = 3
.
prefix_sum_indices = {}
, current_sum = 0
, max_length = 0
current_sum = 1
. Not equal to k. Store 1:0 in map.current_sum = 0
. Not equal to k. Store 0:1 in map.current_sum = 5
. 5 - 3 = 2
not in map. Store 5:2 in map.current_sum = 3
. Now current_sum == k
, so max_length = 4
(from index 0 to 3).current_sum = 6
. 6 - 3 = 3
is in map at index 3. So subarray from 3+1 to 4 (just index 4) sums to k, length is 1. max_length
remains 4.
The answer is 4, corresponding to subarray [1, -1, 5, -2]
.
This problem is elegantly solved with prefix sums and a hash map. By storing the earliest occurrence of each prefix sum, we can instantly check if a previous sum allows us to form a subarray summing to k
. This approach reduces the time complexity from O(n2) to O(n), making it suitable for large arrays. The key insight is recognizing that the difference between two prefix sums gives the sum of the subarray between them.
class Solution:
def maxSubArrayLen(self, nums, k):
prefix_sum_indices = {}
current_sum = 0
max_length = 0
for i, num in enumerate(nums):
current_sum += num
if current_sum == k:
max_length = i + 1
if (current_sum - k) in prefix_sum_indices:
max_length = max(max_length, i - prefix_sum_indices[current_sum - k])
if current_sum not in prefix_sum_indices:
prefix_sum_indices[current_sum] = i
return max_length
class Solution {
public:
int maxSubArrayLen(vector<int>& nums, int k) {
unordered_map<int, int> prefix_sum_indices;
int current_sum = 0, max_length = 0;
for (int i = 0; i < nums.size(); ++i) {
current_sum += nums[i];
if (current_sum == k) {
max_length = i + 1;
}
if (prefix_sum_indices.find(current_sum - k) != prefix_sum_indices.end()) {
max_length = max(max_length, i - prefix_sum_indices[current_sum - k]);
}
if (prefix_sum_indices.find(current_sum) == prefix_sum_indices.end()) {
prefix_sum_indices[current_sum] = i;
}
}
return max_length;
}
};
class Solution {
public int maxSubArrayLen(int[] nums, int k) {
Map<Integer, Integer> prefixSumIndices = new HashMap<>();
int currentSum = 0, maxLength = 0;
for (int i = 0; i < nums.length; i++) {
currentSum += nums[i];
if (currentSum == k) {
maxLength = i + 1;
}
if (prefixSumIndices.containsKey(currentSum - k)) {
maxLength = Math.max(maxLength, i - prefixSumIndices.get(currentSum - k));
}
if (!prefixSumIndices.containsKey(currentSum)) {
prefixSumIndices.put(currentSum, i);
}
}
return maxLength;
}
}
var maxSubArrayLen = function(nums, k) {
let prefixSumIndices = new Map();
let currentSum = 0, maxLength = 0;
for (let i = 0; i < nums.length; i++) {
currentSum += nums[i];
if (currentSum === k) {
maxLength = i + 1;
}
if (prefixSumIndices.has(currentSum - k)) {
maxLength = Math.max(maxLength, i - prefixSumIndices.get(currentSum - k));
}
if (!prefixSumIndices.has(currentSum)) {
prefixSumIndices.set(currentSum, i);
}
}
return maxLength;
};