Add remaining flashcards and reference files (qn_01, segment_tree, ds, learning)
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
#+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<int>& nums) {
|
||||
unordered_map<int,int> 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<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 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<int>& 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<int>& nums, int k) {
|
||||
unordered_map<int,int> 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<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
|
||||
|
||||
* 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
|
||||
@@ -35,25 +35,25 @@ Instead of subtracting, you divide.
|
||||
|
||||
While "subarray + sum" strongly hints at prefix sums, you need to look at the **constraints** and **types of numbers** to choose the absolute best tool.
|
||||
|
||||
| If you see "Subarray + Sum" AND... | The Real Pattern Is... | Why? |
|
||||
|-------------------------------------|------------------------|------|
|
||||
| All numbers are strictly positive (>= 0) and you need to find a target sum or max length. | **Sliding Window (Two Pointers)** | Because the window sum is monotonic (expanding always increases the sum; shrinking always decreases it). Sliding window optimizes space to O(1) compared to O(n) for prefix sums. |
|
||||
| Numbers can be negative, or you need to find an exact target sum. | **Prefix Sum + Hash Map** | Monotonicity is broken. Adding an element could make the sum smaller, so sliding window fails. You *must* use a hash map to remember past states. |
|
||||
| The array is **constantly being updated** (dynamic updates) between sum queries. | **Segment Tree or Fenwick Tree (BIT)** | A standard prefix sum array takes O(n) to update if an element changes. Segment/Fenwick trees drop update and query times to O(log n). |
|
||||
| If you see "Subarray + Sum" AND... | The Real Pattern Is... | Why? |
|
||||
|-------------------------------------------------------------------------------------------+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| All numbers are strictly positive (>= 0) and you need to find a target sum or max length. | **Sliding Window (Two Pointers)** | Because the window sum is monotonic (expanding always increases the sum; shrinking always decreases it). Sliding window optimizes space to O(1) compared to O(n) for prefix sums. |
|
||||
| Numbers can be negative, or you need to find an exact target sum. | **Prefix Sum + Hash Map** | Monotonicity is broken. Adding an element could make the sum smaller, so sliding window fails. You *must* use a hash map to remember past states. |
|
||||
| The array is **constantly being updated** (dynamic updates) between sum queries. | **Segment Tree or Fenwick Tree (BIT)** | A standard prefix sum array takes O(n) to update if an element changes. Segment/Fenwick trees drop update and query times to O(log n). |
|
||||
|
||||
* Generalizing the Concept: "Prefix State"
|
||||
|
||||
Don't limit this trick just to addition. The core philosophy of a prefix sum is **accumulating history as you traverse linearly**. You can use a hash map to track the "prefix state" for things that don't look like math at first.
|
||||
|
||||
| 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 |
|
||||
| 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
|
||||
|
||||
Reference in New Issue
Block a user