#+title: Master Subarray Pattern Sheet * Subarray Divisible by K — Remainder Pattern [algorithm:array] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front Why does "subarray sum divisible by K" work with prefix remainders? ** 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 So we find pairs of indices with the same prefix remainder. Transformation: store current_sum % K in hash map. Data structure: hash map tracking frequencies of remainders. C++ caveat: % can return negative for negative operands. Fix: remainder = ((prefix_sum % K) + K) % K Python: % always non-negative, no fix needed. * Subarray Equal 0s and 1s — Value Mapping [algorithm:array] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front Find the longest subarray with equal number of 0s and 1s. Example: nums = [0,1] → Output: 2 ** Back Replace all 0s with -1. The problem becomes: **"Find a subarray whose sum equals 0."** #+begin_src c++ int findMaxLength(vector& nums) { unordered_map firstOccurrence; firstOccurrence[0] = 0; 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; } #+end_src Time: O(n), Space: O(n) Data structure: hash map tracking raw prefix sums (first occurrence). * Subarray Equal Odd/Even Numbers — Same Pattern [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. The problem becomes: **"Find a subarray whose sum equals 0."** Same approach as equal 0s and 1s: prefix sum + hash map storing first occurrence. This is the same value mapping pattern applied to a different domain. * Subarray Equal Vowels and Consonants [algorithm:array] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front Find the longest subarray with equal number of vowels and consonants. ** Back Map: vowel → +1, consonant → -1. The problem becomes: **"Find a subarray whose sum equals 0."** Same prefix sum + hash map approach. This demonstrates the general principle: any binary-counting problem can be reduced to "subarray sum = 0" via value mapping. * Multi-Category Balance — Equal A/B/C 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. 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, 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 Equals K — Prefix Product [algorithm:array] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front Find the number of subarrays with product equal to K. ** Back Use a **Prefix Product** array: Product(i..j) = PrefixProduct[j] / PrefixProduct[i-1] Instead of subtracting, you divide. Data structure: hash map searching for current_product / K. Edge case: zeros reset the product to 0. Handle by: 1. Segmentation — split the array at zeros, solve each segment independently 2. Track position of last seen zero to reset boundaries Example: subarray product less than K (all positive): #+begin_src c++ int numSubarrayProductLessThanK(vector& nums, int k) { if (k <= 1) 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 Product Positive/Negative — Parity Tracking [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 — first occurrence of even-parity index and first occurrence of odd-parity index. Time: O(n), Space: O(n) * Subarray Bitwise XOR — Prefix XOR [algorithm:array] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front Find the number of subarrays with XOR equal to K. ** Back XOR is invertible (XOR is its own inverse), so the prefix pattern works: XOR(i..j) = PrefixXOR[j] ^ PrefixXOR[i-1] Same hash map pattern as prefix sum: #+begin_src c++ int subarrayXOR(vector& nums, int k) { unordered_map prefixCount; prefixCount[0] = 1; int sum = 0, count = 0; for (int num : nums) { sum ^= num; count += prefixCount[sum ^ k]; prefixCount[sum]++; } return count; } #+end_src Time: O(n), Space: O(n) * Subarray Bitwise OR/AND — Non-Invertible [algorithm:array] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front Can you use prefix sums for subarray problems with Bitwise OR or AND? ** Back **NO.** Unlike XOR, OR/AND are **not invertible**. You cannot "undo" an OR or AND operation. 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& arr) { unordered_set result, current; for (int x : arr) { unordered_set 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 * Master Keyword-to-Algorithm Mapping [algorithm:interview] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** 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) | * Prefix State Generalization — The Unifying Concept [algorithm:concept] :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END: ** Front What is the "prefix state" generalization that unifies all subarray patterns? ** Back The core philosophy of prefix sums is: **accumulate history as you traverse linearly, and use a hash map to track state.** | 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: 1. Define a state that accumulates as you traverse 2. Find a way to "undo" or compare states (subtract, XOR, divide, compare tuples) 3. Use a hash map to find when the same state (or target difference) appears