See [[https://leetcode.com/problems/subarray-sum-equals-k/description/][LC 560]]. Find the total number of continuous subarrays whose sum equals k. Solve using the brute force approach.
See [[https://leetcode.com/problems/subarray-sum-equals-k/description/][LC 560]]. Find the total number of continuous subarrays whose sum equals k. Solve optimally using prefix sum with a hash map.
See [[https://leetcode.com/problems/subarray-sum-equals-k/description/][LC 560]]. In the optimal subarray sum solution (prefix sum + hash map), why is `prefixCount[0] = 1` initialized?
See [[https://leetcode.com/problems/subarray-sums-divisible-by-k/description/][LC 974]]. Find the total number of continuous subarrays whose sum is divisible by k.
See [[https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/description/][LC 325]]. Find the maximum length of a contiguous subarray that sums to k.
See [[https://leetcode.com/problems/subarray-sum-equals-k/description/][LC 560]]. Does the prefix sum + hash map approach for "subarray sum equals k" work when the array contains negative numbers? Why?
See [[https://leetcode.com/problems/subarray-sum-equals-k/description/][LC 560]]. Can you use the sliding window technique to solve "subarray sum equals k"? When does it work?
| Count subarrays sum = K | Any integers | Prefix sum | unordered_map<sum, frequency> | O(n) |
| Longest subarray sum = K | Any integers | Prefix sum | unordered_map<sum, first_index> | O(n) |
| Shortest subarray sum ≥ K | Any integers | Prefix sum + monotonic deque | deque of indices | O(n) |
| Shortest subarray sum = K | Any integers | Prefix sum + ordered map | map<sum, last_index> | O(n log n) |
| Divisible by K | Any integers | Prefix sum + modulo | unordered_map<remainder, freq> | O(n) |
| Sum in range [lower, upper] | Any integers | Prefix sum + BST | multiset / Fenwick / segment tree | O(n log n) |
| Max circular subarray sum | Any integers | Kadane's + total - min_subarray | 2x Kadane | O(n) |
| Subarray sum with only positives | Positives only | Sliding window | Two pointers | O(n) |
| 2D matrix subarray sum = K | 2D grid | Fix rows, compress to 1D | Prefix sum on compressed row | O(n^3) |
| At most K distinct elements | Frequency constraint | Sliding window + freq map | Hash map of counts | O(n) |
| Subarray with exactly K ones | Binary array | Store indices of 1s | Vector of positions | O(n) |
| Two non-overlapping subarrays each = K | Any integers | Prefix sum + track both sides | 2 pass with prefix map | O(n) |
Core pattern: prefix[j] - prefix[i] = subarray(i+1..j). The variation changes what you store and query.
* Subarray Shortest Sum ≥ K — Monotonic Deque [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
See [[https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/description/][LC 862]]. Find the length of the shortest contiguous subarray with sum ≥ K. Works with negative numbers.
Example: nums = [84,-37,32,40,95], K = 167 → Output: 3
** Back
#+begin_src c++
intshortestSubarray(vector<int>&nums,intk){
intn=nums.size();
vector<longlong>prefix(n+1,0);
for(inti=0;i<n;i++)prefix[i+1]=prefix[i]+nums[i];
deque<int>dq;// stores indices, prefix[dq[j]] is increasing
intminLen=INT_MAX;
for(inti=0;i<=n;i++){
// If prefix[i] - prefix[dq.front()] >= K, pop front and record length
Why not hash map? Hash map can't count how many prefix sums fall in a range.
Merge sort counts (i, j) pairs where P[j] - P[i] ∈ [lower, upper] during the merge step.
* Max Circular Subarray Sum [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
See [[https://leetcode.com/problems/maximum-sum-circular-subarray/description/][LC 918]]. Find the maximum sum of a non-empty subarray in a circular array.
// If all numbers are negative, maxSum is the answer (total - minSum = 0)
returnmaxSum<0?maxSum:max(maxSum,total-minSum);
}
#+end_src
Time: O(n), Space: O(1)
Two cases:
1. Maximum subarray does NOT wrap — standard Kadane's (maxSum)
2. Maximum subarray DOES wrap — total - minSubarray (remove the minimum subarray from the middle)
Edge case: all negative → total - minSum = 0, which is wrong. Return maxSum instead.
* 2D Matrix Subarray Sum = K [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
See [[https://leetcode.com/problems/number-of-submatrices-that-sum-to-target/description/][Submatrices Sum to Target]]. Given a 2D matrix, find the number of submatrices whose sum equals K.
Example: matrix = [[0,1,0],[1,1,1],[0,1,0]], K = 0 → Output: 4
Key idea: Fix two rows (r1, r2), compress columns between them into a 1D array.
Then the problem reduces to 1D subarray sum = K.
* Exactly K Distinct Elements with Sum = K [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Find the length of the longest subarray with at most K distinct elements and sum = K.
** Back
#+begin_src c++
intlongestSubarray(vector<int>&nums,intk){
unordered_map<int,int>freq;
intsum=0,distinct=0,maxLen=0;
intleft=0;
for(intright=0;right<nums.size();right++){
if(freq[nums[right]]==0)distinct++;
freq[nums[right]]++;
sum+=nums[right];
// Shrink while distinct > K or sum > k
while(distinct>k||sum>k){
freq[nums[left]]--;
if(freq[nums[left]]==0)distinct--;
sum-=nums[left];
left++;
}
if(sum==k)maxLen=max(maxLen,right-left+1);
}
returnmaxLen;
}
#+end_src
Time: O(n), Space: O(min(n, K))
This combines sliding window (distinct elements constraint) with sum check.
The two constraints (distinct count + sum) make it trickier than simple sliding window.
* Binary Array — Exactly K Ones [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
See [[https://leetcode.com/problems/max-consecutive-ones-iii/description/][LC 1004]]. Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's.
Example: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2 → Output: 6
** Back
#+begin_src c++
intlongestOnes(vector<int>&nums,intk){
intleft=0,maxLen=0;
for(intright=0;right<nums.size();right++){
if(nums[right]==0)k--;
while(k<0){
if(nums[left]==0)k++;
left++;
}
maxLen=max(maxLen,right-left+1);
}
returnmaxLen;
}
#+end_src
Time: O(n), Space: O(1)
Sliding window: expand right, count zeros. When zeros exceed k, shrink from left.
* Two Non-Overlapping Subarrays Each Sum = K [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Find the maximum sum of two non-overlapping subarrays with lengths L and M.
Example: nums = [0,6,5,2,2,5,1,9,4], L = 1, M = 2 → Output: 20 ([9] and [6,5])
* Subarray Sum — Core Trigger Heuristic [algorithm:interview]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
What is the most reliable heuristic for recognizing when to use Prefix Sums?
** Back
When you see **"subarray"** and **"sum"** in the same problem description, **Prefix Sums** should be your immediate first thought.
The core formula:
Sum(i..j) = PrefixSum[j] - PrefixSum[i-1]
This turns a **range query** into a **difference between two points**, eliminating the need to re-scan the array.
The mental trigger is simple: subarray + sum → prefix sums. Always.
* Subarray Sum — When to Use Sliding Window vs Prefix Sum [algorithm:interview]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
When should you use Sliding Window vs Prefix Sum + Hash Map for subarray sum problems?
** Back
| Condition | Use | Why |
|-----------|-----|-----|
| All numbers ≥ 0, need target sum or max length | **Sliding Window** | Monotonic: expanding always increases sum, shrinking always decreases. O(1) space vs O(n). |
| Numbers can be negative, need exact target sum | **Prefix Sum + Hash Map** | Monotonicity broken. Adding an element could make sum smaller. Must remember past states. |
| Frequent updates between queries | **Fenwick Tree / Segment Tree** | Prefix sum array takes O(n) to update. Trees give O(log n) update + query. |
Rule of thumb: check for negative numbers first. If none exist, sliding window wins.
1. Segmentation — split the array at zeros, solve each segment independently
2. Track position of last seen zero to reset boundaries
Example: count subarrays with product < K (all positive):
#+begin_src c++
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if (k == 0) return 0;
int count = 0, product = 1, left = 0;
for (int right = 0; right < nums.size(); right++) {
product *= nums[right];
while (left <= right && product >= k) product /= nums[left++];
count += right - left + 1;
}
return count;
}
#+end_src
* Subarray with Equal 0s and 1s — Value Mapping [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Find the maximum length of a contiguous subarray with equal number of 0s and 1s.
Example: nums = [1,0,1] → Output: 2 ([1,0] or [0,1])
** Back
Treat 0 as -1 and 1 as +1. The problem becomes: **"Find the longest subarray whose sum equals 0."**
#+begin_src c++
intfindMaxLength(vector<int>&nums){
unordered_map<int,int>firstOccurrence;
firstOccurrence[0]=0;// sum 0 at index 0 (1-based)
intsum=0,maxLen=0;
for(inti=0;i<nums.size();i++){
sum+=(nums[i]==1)?1:-1;
if(firstOccurrence.count(sum)){
maxLen=max(maxLen,i+1-firstOccurrence[sum]);
}else{
firstOccurrence[sum]=i+1;
}
}
returnmaxLen;
}
#+end_src
Time: O(n), Space: O(n)
This is the **"prefix state"** generalization: map values to +1/-1, then it's just prefix sum = 0.
* Subarray with Equal Odd and Even Numbers [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Find the longest subarray with equal number of odd and even numbers.
** Back
Map every even number to +1 and every odd number to -1 (or vice versa). The problem becomes:
**"Find the longest subarray whose sum equals 0."**
Same approach as equal 0s and 1s: prefix sum + hash map storing first occurrence.
This is the same "prefix state" generalization applied to a different domain.
* Multi-Category Balance — (A, B, C) Equal Counts [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Find the longest subarray with equal number of 'A's, 'B's, and 'C's.
** Back
You can't use a single scalar (+1/-1) for three categories. Instead, track **relative differences** between counts.
Maintain running counts: c_A, c_B, c_C.
At each step, compute the tuple of differences: (c_A - c_B, c_B - c_C).
If this tuple repeats later in the array, the elements between those indices have perfectly balanced A, B, C counts.
Data structure: hash map where the key is the state tuple:
map<pair<int,int>, int> firstOccurrence;
The tuple (diff_AB, diff_BC) captures the full relative state. If two positions share the same tuple, the subarray between them has zero net change in all three relative differences → equal counts.
Time: O(n), Space: O(n)
* Subarray Product Is Positive / Negative [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Find the maximum length of a subarray with a positive (or negative) product.
** Back
Map: positive → +1, negative → -1, zero → resets the window.
A subarray has positive product if it contains an **even** number of negatives.
A subarray has negative product if it contains an **odd** number of negatives.
Track the parity (odd/even count) of negative numbers as you traverse:
- If parity is even at index i and was even at index j (j < i), the subarray (j+1..i) has positive product.
- If parity is odd at index i and was even at index j, the subarray (j+1..i) has negative product.
Data structure: two hash maps (or arrays) — first occurrence of even-parity index and first occurrence of odd-parity index.
Time: O(n), Space: O(n)
* Bitwise Subarray — OR / AND / XOR [algorithm:array]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
Can you use prefix sums for subarray problems with Bitwise OR, AND, or XOR?
** Back
**XOR:** YES — XOR is invertible (XOR is its own inverse).
XOR(i..j) = PrefixXOR[j] ^ PrefixXOR[i-1]
Same hash map pattern as prefix sum.
**OR / AND:** NO — not invertible. You cannot "undo" an OR or AND operation.
For OR/AND, exploit the key property: as you expand a subarray, the OR/AND result can only change at most **32 times** (for 32-bit integers) because bits only transition 0→1 (OR) or 1→0 (AND).
Strategy: maintain a **set** of all possible OR results ending at the current index. The set never exceeds size 32.
#+begin_src c++
int subarrayBitwiseORs(vector<int>& arr) {
unordered_set<int> result, current;
for (int x : arr) {
unordered_set<int> next;
next.insert(x);
for (int val : current) {
next.insert(val | x);
}
current = next;
for (int val : current) result.insert(val);
}
return result.size();
}
#+end_src
Time: O(n * 32), Space: O(32) per step
* Subarray Sum — Keyword-to-Algorithm Mapping [algorithm:interview]
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
What is the master keyword-to-algorithm mapping for subarray problems?