Back to Arrays
Q41Question 41 of 109

Subarray Sum Equals K: Prefix Sum Plus a HashMap Counting currentSum Minus K

Key Concepts
  • Count subarrays summing to K using a running prefix sum and a HashMap of previously seen prefix-sum counts.
  • The core identity: a subarray from i+1 to j sums to K when prefix[j] - prefix[i] = K, so prefix[i] = currentSum - K.
  • At each index, add to the answer the count of earlier prefixes equal to currentSum - K.
  • Seed the map with prefix sum 0 having count 1 so subarrays starting from index 0 are counted.
  • Add to the answer before recording the current prefix sum to avoid counting a zero-length subarray.
  • Works with negative numbers, unlike a sliding window which assumes monotonically increasing sums.
  • O(n) time and O(n) space for the map, replacing the O(n^2) brute force.

Definition

Keep a running sum and a count of seen sums; at each step add how many times currentSum minus K was seen.
  • Subarray Sum Equals K – counts contiguous subarrays whose elements sum to exactly K, including negative numbers.
  • Prefix-sum identity – a subarray sums to K when prefix[j] - prefix[i] = K, meaning prefix[i] = currentSum - K.
  • HashMap – maps each prefix-sum value to its occurrence count; lookup of currentSum - K gives the number of valid starting points.
  • Seed – map.put(0, 1) handles subarrays starting from index 0.
  • O(n) time, O(n) space – one pass through the array with a map of at most n entries.

The National Bank of India tracks a running net cash position day by day. To count how many date ranges had a net change of exactly K rupees, the clerk keeps a tally of how often each running total has appeared. On each day, if the running total minus K has appeared before, each earlier occurrence pins down a date range that nets exactly K.

Understanding the Concepts

What is the Subarray Sum Equals K problem?

Given an integer array (which may contain negative numbers) and an integer K, count the total number of contiguous subarrays whose elements sum to exactly K. For example, in [1, 1, 1] with K = 2, the answer is 2 (subarrays [1,1] starting at index 0 and index 1).

Why can't a sliding window solve this?

A sliding window assumes that extending the window only increases the sum and shrinking only decreases it. With negative numbers, adding an element might decrease the sum, breaking the monotonic assumption. The prefix-sum-plus-map method works regardless of sign.

How does the prefix-sum-plus-HashMap technique work?

The core identity: a subarray from index i+1 to j sums to K exactly when prefix[j] - prefix[i] = K. Rearranging: prefix[i] = prefix[j] - K = currentSum - K. So at each position j, the number of valid subarrays ending at j equals the number of earlier prefix values equal to currentSum - K.

DIAGRAM
Array: [1, 2, -1, 3]   K = 3

index  value  currentSum  currentSum-K  map(before)        count
  -      -       0            -         {0:1}               0
  0      1       1           -2         {0:1}→{0:1,1:1}     0
  1      2       3            0         {0:1}→count+=1       1
                                        {0:1,1:1,3:1}       
  2     -1       2           -1         {}→count+=0          1
                                        {0:1,1:1,3:1,2:1}
  3      3       5            2         {2:1}→count+=1       2

Answer: 2 (subarrays [1,2] and [2,-1,3])

How do you implement it? (with code)

JAVA
package com.bank.compliance;

import java.util.HashMap;
import java.util.Map;

public class SubarraySumEqualsK {

    public static int countSubarrays(int[] movements, int k) {
        Map<Integer, Integer> prefixCounts = new HashMap<>();
        prefixCounts.put(0, 1);  // seed: prefix sum 0 seen once

        int sum = 0;
        int count = 0;

        for (int movement : movements) {
            sum += movement;
            // How many earlier prefixes equal sum - k?
            count += prefixCounts.getOrDefault(sum - k, 0);
            // Record current prefix sum
            prefixCounts.merge(sum, 1, Integer::sum);
        }
        return count;
    }

    public static void main(String[] args) {
        int[] dailyMovements = {100, 200, -100, 300, -200, 400};
        int target = 300;
        System.out.println("Subarrays summing to " + target + ": "
            + countSubarrays(dailyMovements, target));
    }
}
TEXT
Subarrays summing to 300: 4

Why seed the map with (0, 1)?

The seed handles subarrays that start at index 0. If currentSum equals K at some index, then currentSum - K = 0, and the seeded zero contributes one count. Without the seed, these subarrays would be missed. Forgetting this seed is the single most common bug.

JAVA
// Without seed: misses subarrays starting at index 0
Map<Integer, Integer> map = new HashMap<>();
// map is empty, so sum-K=0 lookup returns 0 even when sum==K

// With seed: correctly counts them
map.put(0, 1);

Why add to count before recording?

You add to the count before recording the current prefix sum so you never count a zero-length subarray (from j to j, exclusive of start). The current sum has not been added to the map yet when you check.

Why store counts instead of just presence?

A given prefix sum can recur multiple times. Each recurrence is a distinct valid starting point for a subarray ending at the current index. Storing only presence (boolean) would undercount.

What are the edge cases?

  • All elements are K – each single element is a valid subarray.
  • K = 0 – count subarrays that sum to zero, including those where elements cancel out.
  • Negative numbers – handled correctly by the prefix-sum technique.
  • Single element equals K – counted correctly via the seed.
  • Empty array – return 0.

How is this different from Two Sum?

Two Sum finds pairs with a given sum in an unordered set. Subarray Sum Equals K finds contiguous ranges with a given sum in a sequential array. Both use a HashMap lookup, but the key in Two Sum is the complement of a single value, while here it is the complement of a running prefix sum.

What are common interview follow-up questions?

  • What about returning the actual subarrays? – Store lists of indices in the map instead of counts.
  • What about the longest subarray summing to K? – Store first occurrence index instead of count (see Contiguous Array).
  • What if all values are positive? – A sliding window works and uses O(1) space.
  • Time and space? – O(n) time, O(n) space.

Visual Overview

DIAGRAM
┌──────────────────────────────────────────────────────┐
│  Subarray Sum Equals K: Prefix Sum + HashMap         │
├──────────────────────────────────────────────────────┤
│                                                      │
│  Identity: prefix[j] - prefix[i] = K                │
│            → prefix[i] = currentSum - K              │
│                                                      │
│  At each j:                                          │
│    1. Compute currentSum += a[j]                     │
│    2. Look up count of (currentSum - K) in map       │
│    3. Add that count to answer                       │
│    4. Record currentSum in map                       │
│                                                      │
│  Seed: map = {0: 1}  (handles subarrays from idx 0) │
│                                                      │
│  Example: [1, 2, -1, 3]  K=3                        │
│  Sums:     1  3   2   5                              │
│  Look up: -2  0  -1   2                              │
│  Hits:     0  1   0   1  → total = 2                │
└──────────────────────────────────────────────────────┘

Key Takeaway

Counting subarrays that sum to exactly K uses a running prefix sum combined with a HashMap that records how many times each prefix-sum value has occurred. At each index, the number of valid subarrays ending here equals the number of earlier prefixes equal to currentSum minus K, because each such prefix marks the start of a subarray summing to K.

Two details are essential. First, seed the map with prefix sum 0 having count 1, which is what lets subarrays starting at index 0 be counted. Forgetting this seed is the most common bug. Second, add to the answer before recording the current prefix sum so you never count a zero-length range.

This method works with negative numbers, which is why it replaces a sliding window. A sliding window assumes extending the window only increases the sum, and that breaks with negatives. The prefix-sum-plus-map technique has no such assumption.

In a banking project at the National Bank of India, compliance monitors a stream of signed daily net cash movements and counts how many contiguous date ranges netted exactly a flagged amount. The linear scan replaces the quadratic brute force of checking every range.

Remember one sentence: Keep a running prefix sum and a HashMap of seen prefix counts; at each position, look up currentSum minus K to count subarrays summing to K in O(n) time.

---

Share
Use ← → arrow keys to navigate