Core Concept of Prefix Sum
Prefix Sum is a technique used to efficiently calculate the sum of subarrays. It allows us to preprocess sums to avoid redundant calculations during queries.
Key formula:
prefixSum[j]−prefixSum[i]=target
Rearrange:
prefixSum[i]=prefixSum[j]−target
Using a HashMap, we store previous prefix sums and their indices, which enables us to find subarrays efficiently.
🔹 Problem Patterns & HashMap Usage
| Pattern** | Problem Type | Key HashMap Concept | HashMap Key (Prefix Sum) | HashMap Value (Stored Information) |
|---|---|---|---|---|
| Fixed sum match | Find shortest subarray with sum == target | Check if prefixSum[j] - target exists in the map | prefixSum values encountered | Index where that prefixSum first appeared |
| Fixed sum count | Count subarrays with sum == target | Count occurrences of prefixSum[j] - target | prefixSum values encountered | Frequency of each prefixSum |
| Modulo condition | Longest subarray with sum % k == 0 | Check if prefixSum[j] % k has appeared before | prefixSum % k values | First occurrence index |
| Balanced sum | Longest contiguous subarray with equal number of 0 and 1 | Convert 0 to -1, check prefixSum[j] | prefixSum values | First occurrence index |
| Closest sum | Subarray with sum closest to target | Use TreeMap for nearest lookup | prefixSum values | Index where that prefixSum first appeared |
🔹 Problem Summaries & Key Insights**
1️⃣ Shortest Subarray with Sum == Target**
- Key Idea:Maintain a prefix sum while iterating, store previous sums in a HashMap.
- HashMap Stores:
(prefixSum → first index where it appears) - Logic: If
prefixSum[j] - targetexists in the map, we update the shortest length.
public int shortestSubarray(int[] nums, int target) {
int shortest = Integer.MAX_VALUE, sum = 0;
Map<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
for (int j = 0; j < nums.length; j++) {
sum += nums[j];
if (map.containsKey(sum - target)) {
shortest = Math.min(shortest, j - map.get(sum - target));
}
map.put(sum, j);
}
return shortest == Integer.MAX_VALUE ? -1 : shortest;
}
✅ Use case:Find a fixed-length window with a required sum.
2️⃣ Longest Subarray with Sum == Target**
- Key Idea: Use HashMap to store the first occurrence of each prefix sum.
- HashMap Stores:
(prefixSum → first occurrence index) - Logic: If
prefixSum[j] - targetexists, update longest length.
public int longestSubarray(int[] nums, int target) { int longest = Integer.MIN_VALUE, sum = 0; Map<Integer, Integer> map = new HashMap<>(); map.put(0, -1);
for (int j = 0; j < nums.length; j++) { sum += nums[j]; if (map.containsKey(sum - target)) { longest = Math.max(longest, j - map.get(sum - target)); } map.putIfAbsent(sum, j); } return longest == Integer.MIN_VALUE ? 0 : longest; }
✅ Use case: Finding the longest matching subarray.
3️⃣ Count Subarrays with Sum == Target**
- Key Idea:Instead of finding a single match, count all occurrences where
prefixSum[j] - targetappeared before. - HashMap Stores: (prefixSum → frequency count)`
- Logic: If
prefixSum[j] - targetis in the map, add its frequency to the count.
public int countSubarray(int[] nums, int target) {
int result = 0, sum = 0;
Map<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
for (int j = 0; j < nums.length; j++) {
sum += nums[j];
result += map.getOrDefault(sum - target, 0);
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return result;
}
✅ Use case:Counting frequency of subarrays meeting a condition.
4️⃣ Subarray with Sum Closest to Target
- Key Idea: Instead of exact matches, find the closest
prefixSum[i]using a TreeMap. - TreeMap Stores:
(prefixSum → index) - Logic: Use
floorKey()andceilingKey()to find the nearest sum.
public int closestSubarray(int[] nums, int target) {
int closest = Integer.MAX_VALUE, sum = 0;
TreeMap<Integer, Integer> prefixMap = new TreeMap<>();
prefixMap.put(0, -1);
for (int j = 0; j < nums.length; j++) {
sum += nums[j];
Integer floor = prefixMap.floorKey(sum - target);
Integer ceil = prefixMap.ceilingKey(sum - target);
if (floor != null) closest = Math.min(closest, Math.abs(sum - floor - target));
if (ceil != null) closest = Math.min(closest, Math.abs(sum - ceil - target));
prefixMap.put(sum, j);
}
return closest;
}
✅ Use case: Finding the closest sum rather than an exact match.
5️⃣ Longest Subarray Where Sum % k == 0
- Key Idea: If two prefix sums have the same remainder when divided by
k, their difference is divisible byk. - HashMap Stores:
(prefixSum % k → first occurrence index)
public int longestSubarrayDivK(int[] nums, int k) {
int longest = 0, sum = 0;
Map<Integer, Integer> remainderIndex = new HashMap<>();
remainderIndex.put(0, -1);
for (int j = 0; j < nums.length; j++) {
sum += nums[j];
int remainder = sum % k;
if (remainder < 0) remainder += k; // Handle negative cases
if (remainderIndex.containsKey(remainder)) {
longest = Math.max(longest, j - remainderIndex.get(remainder));
} else {
remainderIndex.put(remainder, j);
}
}
return longest;
}
✅ Use case:Finding divisibility conditions in subarrays.
6️⃣ Contiguous Array (Equal 0s and 1s)**
- Key Idea:Convert
0to-1and use prefix sum. - HashMap Stores:
(prefixSum → first occurrence index)
public int findMaxLength(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
int maxLength = 0, prefixSum = 0;
for (int i = 0; i < nums.length; i++) {
prefixSum += (nums[i] == 1) ? 1 : -1;
if (map.containsKey(prefixSum)) {
maxLength = Math.max(maxLength, i - map.get(prefixSum));
} else {
map.put(prefixSum, i);
}
}
return maxLength;
}
✅ Use case: Finding the longest balanced subarray.
🔹 Key Differences & Common Mistakes
| Type | Exact Match | Frequency Count | Closest Match | Modulo Condition |
|---|---|---|---|---|
| Method | prefixSum[j] - target == prefixSum[i] | count += map.get(prefixSum[j] - target) | floorKey() & ceilingKey() | prefixSum[j] % k == prefixSum[i] % k |
| HashMap Use | First occurrence index | Frequency count | Ordered Map (TreeMap) | First occurrence index |
✅ AI & ML Value:
- Time-series forecasting (e.g., anomaly detection in logs)
- NLP Sequence Analysis (e.g., detecting patterns in text)
- Fraud Detection (e.g., detecting abnormal spending sequences)
🔹 Summary
- Use HashMap for exact match problems (shortest, longest, count)
- Use TreeMap for closest match problems (binary search needs)
- Modulo-based problems leverage remainder relationships
- Common mistakes: Not handling negative cases, incorrect prefix sum initialization.
**Mastering Prefix Sum + HashMap is essential for solving a broad range of subarray problems efficiently! 🚀