27 KiB
Subarrays with Sum Equal to K
- Subarray Sum Equals K - Brute Force [algorithm:array]
- Subarray Sum Equals K - Prefix Sum + Hash Map [algorithm:array]
- Subarray Sum Equals K - Why prefixCount[0] = 1 [algorithm:interview]
- Subarray Sum Divisible by K [algorithm:array]
- Longest Subarray Sum Equals K [algorithm:array]
- Subarray Sum Equals K - Negative Numbers? [algorithm:interview]
- Subarray Sum Equals K - Sliding Window Limitation [algorithm:interview]
- Subarray Sum Variations — Master Table [algorithm:array]
- Subarray Shortest Sum ≥ K — Monotonic Deque [algorithm:array]
- Subarray Sum in Range [lower, upper] [algorithm:array]
- Max Circular Subarray Sum [algorithm:array]
- 2D Matrix Subarray Sum = K [algorithm:array]
- Exactly K Distinct Elements with Sum = K [algorithm:array]
- Binary Array — Exactly K Ones [algorithm:array]
- Two Non-Overlapping Subarrays Each Sum = K [algorithm:array]
- Subarray Sum — All Relationships Diagram [algorithm:interview]
- Subarray Sum — Core Trigger Heuristic [algorithm:interview]
- Subarray Sum — When to Use Sliding Window vs Prefix Sum [algorithm:interview]
- Subarray Product — Prefix Product Pattern [algorithm:array]
- Subarray with Equal 0s and 1s — Value Mapping [algorithm:array]
- Subarray with Equal Odd and Even Numbers [algorithm:array]
- Multi-Category Balance — (A, B, C) Equal Counts [algorithm:array]
- Subarray Product Is Positive / Negative [algorithm:array]
- Bitwise Subarray — OR / AND / XOR [algorithm:array]
- Subarray Sum — Keyword-to-Algorithm Mapping [algorithm:interview]
- Subarray Sum — Modular Arithmetic Insight [algorithm:interview]
- Subarray Sum — Prefix State Generalization [algorithm:concept]
Subarray Sum Equals K - Brute Force [algorithm:array]
Front
See LC 560. Find the total number of continuous subarrays whose sum equals k. Solve using the brute force approach. Example: nums = [1,2,3], k = 3 → Output: 2 ([1,2] and [3])
Back
int subarraySum(vector<int>& nums, int k) {
int count = 0;
for (int i = 0; i < nums.size(); i++) {
int sum = 0;
for (int j = i; j < nums.size(); j++) {
sum += nums[j];
if (sum == k) count++;
}
}
return count;
}
Time: O(n^2), Space: O(1)
Subarray Sum Equals K - Prefix Sum + Hash Map [algorithm:array]
Front
See LC 560. Find the total number of continuous subarrays whose sum equals k. Solve optimally using prefix sum with a hash map. Example: nums = [1,2,3], k = 3 → Output: 2
Back
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> prefixCount;
prefixCount[0] = 1; // base case: sum of 0 appears once
int sum = 0, count = 0;
for (int num : nums) {
sum += num;
// If (sum - k) exists, those prefixes form valid subarrays
count += prefixCount[sum - k];
prefixCount[sum]++;
}
return count;
}
Time: O(n), Space: O(n)
Subarray Sum Equals K - Why prefixCount[0] = 1 [algorithm:interview]
Front
See LC 560. In the optimal subarray sum solution (prefix sum + hash map), why is `prefixCount[0] = 1` initialized?
Back
It handles the case where a subarray starts from index 0 and its sum equals k.
When sum == k at some index, we look up prefixCount[sum - k] = prefixCount[0]. Without the initialization, this lookup would return 0, missing valid subarrays like [3] where k = 3.
The base case means: "a prefix sum of 0 exists once (before any element)" — conceptually the empty prefix.
Subarray Sum Divisible by K [algorithm:array]
Front
See LC 974. Find the total number of continuous subarrays whose sum is divisible by k. Example: nums = [23,2,4,6,7], k = 6 → Output: 2 ([2,4] and [7])
Back
int subarraysDivByK(vector<int>& nums, int k) {
unordered_map<int,int> prefixCount;
prefixCount[0] = 1;
int sum = 0, count = 0;
for (int num : nums) {
sum += num;
// Modulo can be negative in C++, fix with ((sum % k) + k) % k
int remainder = ((sum % k) + k) % k;
count += prefixCount[remainder];
prefixCount[remainder]++;
}
return count;
}
Key insight: If two prefix sums have the same remainder mod k, their subarray sum is divisible by k.
Time: O(n), Space: O(min(n, k))
Longest Subarray Sum Equals K [algorithm:array]
Front
See LC 325. Find the maximum length of a contiguous subarray that sums to k. Example: nums = [1,-1,5,-2,3], k = 3 → Output: 4 ([1,-1,5,-2])
Back
int maxSubArrayLen(vector<int>& nums, int k) {
unordered_map<int,int> firstOccurrence;
firstOccurrence[0] = 0; // sum 0 at index 0 (1-based)
int sum = 0, maxLen = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
// Check if subarray ending here sums to k
if (firstOccurrence.count(sum - k)) {
maxLen = max(maxLen, i + 1 - firstOccurrence[sum - k]);
}
// Store first occurrence only (for longest)
if (!firstOccurrence.count(sum)) {
firstOccurrence[sum] = i + 1;
}
}
return maxLen;
}
Key difference from counting: store only the first occurrence of each prefix sum to maximize length.
Time: O(n), Space: O(n)
Subarray Sum Equals K - Negative Numbers? [algorithm:interview]
Front
See LC 560. Does the prefix sum + hash map approach for "subarray sum equals k" work when the array contains negative numbers? Why?
Back
Yes, it works with negative numbers.
The prefix sum approach does NOT require positive-only elements. It works for any integer array because:
- prefix[j] - prefix[i] = sum[i+1..j] is always true regardless of sign
- The hash map tracks ALL prefix sums seen, positive or negative
- We only need (sum - k) to exist in the map
This makes it superior to the sliding window approach, which only works for non-negative arrays.
Subarray Sum Equals K - Sliding Window Limitation [algorithm:interview]
Front
See LC 560. Can you use the sliding window technique to solve "subarray sum equals k"? When does it work?
Back
Sliding window ONLY works when all elements are non-negative (positive or zero).
When all elements >= 0:
- Expanding the window increases the sum
- Shrinking the window decreases the sum
- This monotonicity lets us adjust the window
Sliding window fails with negative numbers because adding/removing elements doesn't monotonically change the sum.
Prefer prefix sum + hash map — it works for all cases (positive, negative, zero).
Subarray Sum Variations — Master Table [algorithm:array]
Front
What's the master table of subarray sum variations and their approaches?
Back
| Question | Constraint | Approach | Key Data Structure | Time |
|---|---|---|---|---|
| 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]
Front
See 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
int shortestSubarray(vector<int>& nums, int k) {
int n = nums.size();
vector<long long> prefix(n + 1, 0);
for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + nums[i];
deque<int> dq; // stores indices, prefix[dq[j]] is increasing
int minLen = INT_MAX;
for (int i = 0; i <= n; i++) {
// If prefix[i] - prefix[dq.front()] >= K, pop front and record length
while (!dq.empty() && prefix[i] - prefix[dq.front()] >= k) {
minLen = min(minLen, i - dq.front());
dq.pop_front();
}
// Maintain monotonic increasing order of prefix values in deque
while (!dq.empty() && prefix[i] < prefix[dq.back()]) {
dq.pop_back();
}
dq.push_back(i);
}
return minLen == INT_MAX ? -1 : minLen;
}
Time: O(n), Space: O(n)
Key insight: Maintain a deque of indices where prefix sums are increasing. If prefix[i] - prefix[dq.front()] ≥ K, then any index after dq.front() gives a shorter valid subarray.
Subarray Sum in Range [lower, upper] [algorithm:array]
Front
See LC 327. Count the number of subarrays whose sum lies in range [lower, upper]. Example: nums = [-2,5,-1], lower = -2, upper = 2 → Output: 3 ([-2], [-1], [5,-1])
Back
int countRangeSum(vector<int>& nums, int lower, int upper) {
int n = nums.size();
vector<long long> prefix(n + 1, 0);
for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + nums[i];
// Use merge sort to count valid pairs
return mergeCount(prefix, 0, n, lower, upper);
}
int mergeCount(vector<long long>& P, int lo, int hi, int lower, int upper) {
if (lo >= hi - 1) return 0;
int mid = lo + (hi - lo) / 2;
int count = mergeCount(P, lo, mid, lower, upper)
+ mergeCount(P, mid, hi, lower, upper);
// Count valid (i, j) pairs where P[j] - P[i] in [lower, upper]
int j = mid, k = mid, t = mid;
for (int i = lo; i < mid; i++) {
while (k <= hi && P[k] - P[i] < lower) k++;
while (t <= hi && P[t] - P[i] <= upper) t++;
count += (t - k);
}
// Merge step
vector<long long> temp;
int p1 = lo, p2 = mid;
while (p1 < mid && p2 < hi) {
if (P[p1] <= P[p2]) temp.push_back(P[p1++]);
else temp.push_back(P[p2++]);
}
while (p1 < mid) temp.push_back(P[p1++]);
while (p2 < hi) temp.push_back(P[p2++]);
for (int i = 0; i < (int)temp.size(); i++) P[lo + i] = temp[i];
return count;
}
Time: O(n log n), Space: O(n)
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]
Front
See LC 918. Find the maximum sum of a non-empty subarray in a circular array. Example: nums = [1,-2,3,-2] → Output: 3 Example: nums = [5,-3,5] → Output: 10 (wraps around: [5, 5])
Back
int maxSubarraySumCircular(vector<int>& nums) {
int total = 0, maxSum = nums[0], curMax = 0;
int minSum = nums[0], curMin = 0;
for (int num : nums) {
curMax = max(curMax + num, num);
maxSum = max(maxSum, curMax);
curMin = min(curMin + num, num);
minSum = min(minSum, curMin);
total += num;
}
// If all numbers are negative, maxSum is the answer (total - minSum = 0)
return maxSum < 0 ? maxSum : max(maxSum, total - minSum);
}
Time: O(n), Space: O(1)
Two cases:
- Maximum subarray does NOT wrap — standard Kadane's (maxSum)
- 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]
Front
See 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
Back
int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int count = 0;
// Fix top and bottom rows, compress to 1D array
for (int r1 = 0; r1 < m; r1++) {
vector<int> colSum(n, 0);
for (int r2 = r1; r2 < m; r2++) {
// Add row r2 to the compressed column sums
for (int c = 0; c < n; c++) {
colSum[c] += matrix[r2][c];
}
// Now solve 1D subarray sum = target
unordered_map<int,int> prefixCount;
prefixCount[0] = 1;
int sum = 0;
for (int val : colSum) {
sum += val;
count += prefixCount[sum - target];
prefixCount[sum]++;
}
}
}
return count;
}
Time: O(m^2 * n), Space: O(n)
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]
Front
Find the length of the longest subarray with at most K distinct elements and sum = K.
Back
int longestSubarray(vector<int>& nums, int k) {
unordered_map<int,int> freq;
int sum = 0, distinct = 0, maxLen = 0;
int left = 0;
for (int right = 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);
}
return maxLen;
}
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]
Front
See 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
int longestOnes(vector<int>& nums, int k) {
int left = 0, maxLen = 0;
for (int right = 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);
}
return maxLen;
}
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]
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])
Back
int maxSumTwoNoOverlap(vector<int>& nums, int L, int M) {
int n = nums.size();
// Prefix sums for O(1) subarray sum queries
vector<int> prefix(n + 1, 0);
for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + nums[i];
auto sum = [&](int i, int j) { return prefix[j + 1] - prefix[i]; };
int maxL = 0, maxM = 0, result = 0;
// Case 1: L comes before M
for (int i = L + M; i <= n; i++) {
maxL = max(maxL, sum(i - L - M, i - M - 1));
maxM = max(maxM, sum(i - M, i - 1));
result = max(result, maxL + maxM);
}
// Case 2: M comes before L
maxL = 0; maxM = 0; result = 0;
for (int i = L + M; i <= n; i++) {
maxM = max(maxM, sum(i - L - M, i - L - 1));
maxL = max(maxL, sum(i - L, i - 1));
result = max(result, maxL + maxM);
}
return result;
}
Time: O(n), Space: O(n)
Two cases: L before M, or M before L. Track the running maximum of the first subarray while computing the second.
Subarray Sum — All Relationships Diagram [algorithm:interview]
Front
What's the decision tree for choosing the right subarray sum approach?
Back
- Are there negative numbers? → YES: Prefix sum approach (hash map, BST, or merge sort) → NO: Sliding window is possible
- What are you counting? → Count all: hash_map<sum, frequency> → Longest: hash_map<sum, first_index> → Shortest: hash_map<sum, last_index> or monotonic deque
- Is the target a range [lower, upper]? → YES: Merge sort (count pairs in range) or Fenwick tree / BST
- Is it divisible by K? → YES: hash_map<remainder, frequency> with modulo
- Is the array circular? → YES: Kadane's twice — max(max_subarray, total - min_subarray)
- Is it 2D? → YES: Fix 2 rows, compress to 1D, then prefix sum
- Are elements only 0/1? → YES: Store indices of 1s, or sliding window counting zeros
- Are there constraints on distinct elements? → YES: Sliding window + frequency map
- Are there multiple non-overlapping subarrays? → YES: 2-pass — track running max of first while computing second
The fundamental identity: subarray(i,j) = prefix[j+1] - prefix[i]. Everything else is about what you do with the prefix array.
Subarray Sum — Core Trigger Heuristic [algorithm:interview]
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]
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.
Subarray Product — Prefix Product Pattern [algorithm:array]
Front
How does the prefix sum pattern translate to subarray product problems?
Back
The prefix sum pattern translates directly into a Prefix Product:
Product(i..j) = PrefixProduct[j] / PrefixProduct[i-1]
Instead of subtracting, you divide.
Edge case: zeros break division. Handle by:
- Segmentation — split the array at zeros, solve each segment independently
- Track position of last seen zero to reset boundaries
Example: count subarrays with product < K (all positive):
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;
}
Subarray with Equal 0s and 1s — Value Mapping [algorithm:array]
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."
int findMaxLength(vector<int>& nums) {
unordered_map<int,int> firstOccurrence;
firstOccurrence[0] = 0; // sum 0 at index 0 (1-based)
int sum = 0, maxLen = 0;
for (int i = 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;
}
}
return maxLen;
}
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]
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]
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]
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]
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.
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();
}
Time: O(n * 32), Space: O(32) per step
Subarray Sum — Keyword-to-Algorithm Mapping [algorithm:interview]
Front
What is the master keyword-to-algorithm mapping for subarray problems?
Back
| Problem Phrase | Array Property | Algorithm |
|---|---|---|
| "Continuous subarray + Sum = K" | Only positive numbers | Sliding Window (O(1) space) |
| "Continuous subarray + Sum = K" | Positive & negative | Prefix Sum + Hash Map (O(n) space) |
| "Divisible by K" or "Multiple of X" | Any numbers | Prefix Remainder + Hash Map (sum % K) |
| "Equal number of X and Y" | Any numbers | Value Mapping (X→1, Y→-1) + Prefix Sum Map |
| "Maximum / Minimum Sum" | Any numbers | Kadane's Algorithm (DP) |
| "Subarray Sum + Frequent Updates" | Element mutations | Fenwick Tree / Segment Tree |
| "Subarray product = K" | No zeros | Prefix Product (division) |
| "Subarray product positive/negative" | Any numbers | Parity tracking of negative count |
| "Subarray XOR = K" | Any numbers | Prefix XOR + Hash Map |
| "Subarray OR / AND" | Any numbers | Set of results (bounded by 32 changes) |
Subarray Sum — Modular Arithmetic Insight [algorithm:interview]
Front
Why does "subarray sum divisible by K" work with prefix remainders? Prove it.
Back
If Sum(i..j) mod K = 0, then: (Prefix[j] - Prefix[i-1]) mod K = 0
By modular arithmetic: Prefix[j] mod K = Prefix[i-1] mod K
Proof: Prefix[j] - Prefix[i-1] = m * K (for some integer m) Prefix[j] = Prefix[i-1] + m * K Prefix[j] mod K = (Prefix[i-1] + m * K) mod K Prefix[j] mod K = Prefix[i-1] mod K (since m*K mod K = 0)
So we just need to find pairs of indices with the same prefix remainder.
Caveat: In C++/Java, % can return negative values for negative operands. Fix: remainder = ((prefix_sum % K) + K) % K
In Python, % always returns non-negative, so no fix needed.
Subarray Sum — Prefix State Generalization [algorithm:concept]
Front
What is the "prefix state" generalization, and how does it extend beyond addition?
Back
The core philosophy of prefix sums is: accumulate history as you traverse linearly, and use a hash map to track state.
This generalizes far beyond addition:
| Problem | Mapping | Reduces To |
|---|---|---|
| Equal 0s and 1s | 0→-1, 1→+1 | Subarray sum = 0 |
| Equal odd/even | even→+1, odd→-1 | Subarray sum = 0 |
| Equal vowels/consonants | vowel→+1, consonant→-1 | Subarray sum = 0 |
| Equal A/B/C counts | Track (c_A-c_B, c_B-c_C) | Prefix state tuple repeats |
| Subarray product | Prefix product | Division (handle zeros) |
| Subarray XOR | Prefix XOR | XOR is invertible |
| Subarray sum | Prefix sum | Subtraction |
The pattern:
- Define a state that accumulates as you traverse
- Find a way to "undo" or compare states (subtract, XOR, divide, compare tuples)
- Use a hash map to find when the same state (or target difference) appears