diff --git a/data/leetcode-problems.json b/data/leetcode-problems.json index 1aaa252..aeccf99 100644 --- a/data/leetcode-problems.json +++ b/data/leetcode-problems.json @@ -25839,5 +25839,6 @@ "2120": {"id": 2120, "category": "Simulation", "title": "Execution of All Suffix Instructions Staying in a Grid", "difficulty": "Medium", "link": "https://leetcode.com/problems/execution-of-all-suffix-instructions-staying-in-a-grid/", "tags": []}, "2221": {"id": 2221, "category": "Array", "title": "Find Triangular Sum of an Array", "difficulty": "Medium", "link": "https://leetcode.com/problems/find-triangular-sum-of-an-array/", "tags": []}, "1605": {"id": 1605, "category": "Matrix", "title": "Find Valid Matrix Given Row and Column Sums", "difficulty": "Medium", "link": "https://leetcode.com/problems/find-valid-matrix-given-row-and-column-sums/", "tags": []}, - "1551": {"id": 1551, "category": "Math", "title": "Minimum Operations to Make Array Equal", "difficulty": "Medium", "link": "https://leetcode.com/problems/minimum-operations-to-make-array-equal/", "tags": []} + "1551": {"id": 1551, "category": "Math", "title": "Minimum Operations to Make Array Equal", "difficulty": "Medium", "link": "https://leetcode.com/problems/minimum-operations-to-make-array-equal/", "tags": []}, + "3562": {"id": 3562, "category": "Dynamic Programming", "title": "Maximum Profit from Trading Stocks with Discounts", "difficulty": "Hard", "link": "https://leetcode.com/problems/maximum-profit-from-trading-stocks-with-discounts/", "tags": []} } diff --git a/explanations/1008/en.md b/explanations/1008/en.md index b502b4b..33fa65c 100644 --- a/explanations/1008/en.md +++ b/explanations/1008/en.md @@ -2,51 +2,55 @@ ### Strategy -**Restate the problem** -Given preorder traversal of a BST, reconstruct the tree. - -**1.1 Constraints & Complexity** -- **Input Size:** up to 100 nodes. -- **Time Complexity:** O(n) using a stack to place nodes. -- **Space Complexity:** O(n) for the stack/tree nodes. -- **Edge Case:** Single-node preorder list. - -**1.2 High-level approach** -Iterate preorder; use a stack of ancestors. Each value smaller than stack top goes left; otherwise pop until finding parent, then attach right. -![BST reconstruction from preorder](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) - -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Insert each value via BST insert — still O(n²) worst case (sorted input). -- **Optimized:** Monotonic stack to place nodes in O(n). - -**1.4 Decomposition** -1. Create root from first value; push to stack. -2. For each next value: - - If value < stack top, set as left child of top. - - Else pop until stack empty or top > value; last popped is parent; attach as right child. -3. Push new node to stack. -4. Return root. +**Constraints & Edge Cases** + +* **Preorder Array:** The preorder traversal array has length 1-100, with unique values between 1-1000. The array is guaranteed to form a valid BST. +* **Time Complexity:** We process each element once, and for each element we may need to find the split point. In worst case, this is O(n²), but average case is O(n log n). **Time Complexity: O(n²)** worst case, **O(n log n)** average, **Space Complexity: O(n)** for recursion stack. +* **Edge Case:** If the array has only one element, we return a single node tree. + +**High-level approach** + +The problem asks us to construct a BST from its preorder traversal. In preorder, the root comes first, followed by all left subtree nodes, then all right subtree nodes. + +**Brute force vs. optimized strategy** + +* **Brute Force:** For each element, insert it into the BST one by one. This would be O(n²) time. +* **Optimized:** Use the property that in preorder, after the root, all values less than root form the left subtree, and all values greater than root form the right subtree. Recursively build left and right subtrees. + +**Decomposition** + +1. **Root Selection:** The first element in preorder is always the root. +2. **Split Point:** Find where values transition from less than root to greater than root. +3. **Recursive Construction:** Build left subtree from elements before split, right subtree from elements after split. ### Steps -**2.1 Initialization & Example Setup** -Example: `[8,5,1,7,10,12]`; root = 8, stack = [8]. +1. **Initialization & Example Setup** + Let's use `preorder = [8,5,1,7,10,12]` as our example. + - If preorder is empty, return None. + +2. **Root Creation** + - Create root node with value `preorder[0] = 8`. -**2.2 Start Checking** -Process each value, updating stack and children. +3. **Find Split Point** + - Start from index 1, find the first index where `preorder[i] >= preorder[0]`. + - For our example: `preorder[1]=5 < 8`, `preorder[2]=1 < 8`, `preorder[3]=7 < 8`, `preorder[4]=10 >= 8`. + - Split point `i = 4`. -**2.3 Trace Walkthrough** -| val | Stack before | Action | Child | -|-----|--------------|---------------------------------|--------| -| 5 | [8] | 5 < 8 → left of 8 | left | -| 1 | [8,5] | 1 < 5 → left of 5 | left | -| 7 | [8,5,1] | pop 1,5 (last popped=5) → right | right | -| 10 | [8,7] | pop 7,8 (last popped=8) → right | right | -| 12 | [10] | pop none → right of 10 | right | +4. **Trace Walkthrough** -**2.4 Increment and Loop** -Continue until all preorder values are attached. +| Step | Left Subtree | Root | Right Subtree | Action | +|------|--------------|------|----------------|--------| +| 1 | [5,1,7] | 8 | [10,12] | Create root(8) | +| 2 | [1] | 5 | [7] | Build left: root(5) | +| 3 | [] | 1 | [] | Build left of 5: root(1) | +| 4 | [] | 7 | [] | Build right of 5: root(7) | +| 5 | [] | 10 | [12] | Build right: root(10) | +| 6 | [] | 12 | [] | Build right of 10: root(12) | -**2.5 Return Result** -Root of the constructed BST. +5. **Recursive Construction** + - `root.left = bstFromPreorder([5,1,7])` → builds left subtree + - `root.right = bstFromPreorder([10,12])` → builds right subtree +6. **Return Result** + Return the root node of the constructed BST. diff --git a/explanations/1079/en.md b/explanations/1079/en.md index 28819c5..e20fa17 100644 --- a/explanations/1079/en.md +++ b/explanations/1079/en.md @@ -2,49 +2,57 @@ ### Strategy -**Restate the problem** -Count all non-empty sequences that can be formed from the multiset of tiles (letters), respecting available counts. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= len(tiles) <= 7`. -- **Time Complexity:** O(n! * n) in worst case (backtracking over permutations), acceptable for n <= 7. -- **Space Complexity:** O(n) recursion depth + O(1) counts. -- **Edge Case:** Single tile → exactly 1 sequence. +* **String Length:** The tiles string has length 1-7, consisting of uppercase English letters. This small constraint allows for backtracking solutions. +* **Time Complexity:** We generate all possible sequences using backtracking. The number of sequences depends on the character frequencies. **Time Complexity: O(2^n)** where n is the number of unique sequences, **Space Complexity: O(n)** for recursion stack and character counts. +* **Edge Case:** If tiles has only one character, there's exactly one sequence (the character itself). -**1.2 High-level approach** -Use backtracking with frequency counts; at each step, pick a letter with remaining count, decrement, recurse, then restore. -![Backtracking over letter counts](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Generate all permutations with duplicates and then deduplicate — costly. -- **Optimized:** Use counts to avoid generating identical branches; count as we go. +The problem asks us to count all possible non-empty sequences we can form from the given tiles. Each tile can be used at most as many times as it appears in the original string. -**1.4 Decomposition** -1. Build frequency map of letters. -2. DFS: for each letter with count > 0, use it (res++), decrement, recurse, restore. -3. Sum all counts encountered. -4. Return `res`. +**Brute force vs. optimized strategy** -### Steps - -**2.1 Initialization & Example Setup** -Example: `tiles = "AAB"`; counts: A:2, B:1, `res=0`. +* **Brute Force:** Generate all possible sequences and count them. This is what we do, but we use backtracking to avoid duplicates efficiently. +* **Optimized:** Use backtracking with character frequency counting. For each character, we can either use it (if available) or skip it. Count each valid sequence as we build it. -**2.2 Start Checking** -Try each available letter, recurse with updated counts. +**Decomposition** -**2.3 Trace Walkthrough** -| Path | Counts after pick | res increment | Notes | -|---------|-------------------|---------------|--------------| -| A | A:1,B:1 | +1 | recurse more | -| AA | A:0,B:1 | +1 | recurse | -| AAB | A:0,B:0 | +1 | dead end | -| AB | A:1,B:0 | +1 | recurse | -| ... | ... | ... | ... | +1. **Count Characters:** Create a frequency map of all characters in tiles. +2. **Backtrack:** For each character, try using it (if available) and recursively count sequences. +3. **Count Sequences:** Each time we add a character, we have a new sequence, so increment the count. -**2.4 Increment and Loop** -Each pick adds 1 to `res` (for the new sequence) before deeper recursion. - -**2.5 Return Result** -Final `res = 8` for the example. +### Steps +1. **Initialization & Example Setup** + Let's use `tiles = "AAB"` as our example. + - Count characters: `count = {'A': 2, 'B': 1}`. + +2. **Backtrack Function** + The `backtrack(count)` function: + - Initialize `res = 0` to count sequences. + - For each character in count: + - If count[char] > 0, we can use it. + - Increment `res` by 1 (this character alone is a sequence). + - Decrement count[char], recursively call backtrack, then restore count[char]. + +3. **Trace Walkthrough** + +Starting with `count = {'A': 2, 'B': 1}`: + +| Step | Character | Count After | Sequences Found | Action | +|------|-----------|-------------|-----------------|--------| +| 1 | 'A' | {'A':1,'B':1} | 1 ("A") | Use A, recurse | +| 2 | 'A' | {'A':0,'B':1} | 1 ("AA") | Use A again, recurse | +| 3 | 'B' | {'A':0,'B':0} | 1 ("AAB") | Use B, recurse | +| 4 | 'B' | {'A':1,'B':0} | 1 ("AB") | Backtrack, use B instead | +| 5 | 'A' | {'A':0,'B':0} | 1 ("ABA") | Use A, recurse | +| 6 | 'B' | {'A':2,'B':0} | 1 ("B") | Backtrack, use B first | +| 7 | 'A' | {'A':1,'B':0} | 1 ("BA") | Use A, recurse | +| 8 | 'A' | {'A':0,'B':0} | 1 ("BAA") | Use A again | + +Total: 8 sequences ("A", "AA", "AAB", "AB", "ABA", "B", "BA", "BAA") + +4. **Return Result** + Return the total count from backtrack function. diff --git a/explanations/1261/en.md b/explanations/1261/en.md index 165aa16..cb1a127 100644 --- a/explanations/1261/en.md +++ b/explanations/1261/en.md @@ -2,47 +2,51 @@ ### Strategy -**Restate the problem** -Recover a contaminated binary tree where original root was 0 and children follow `left = 2*x+1`, `right = 2*x+2`. Support queries to check if a target value exists. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** Up to `1e4` nodes, height <= 20. -- **Time Complexity:** O(n) to recover; O(1) average for `find` via set lookup. -- **Space Complexity:** O(n) to store recovered values. -- **Edge Case:** Single-node tree. +* **Tree Structure:** The binary tree has a specific structure where root.val = 0, and for any node with value x, left child has value 2*x+1 and right child has value 2*x+2. The tree height is at most 20. +* **Time Complexity:** Initialization takes O(n) time to recover all values, where n is the number of nodes. Each find operation is O(1) with a hash set. **Time Complexity: O(n)** for initialization, **O(1)** for find, **Space Complexity: O(n)** for storing values. +* **Edge Case:** If the tree is empty or has only one node, the recovery still works correctly. -**1.2 High-level approach** -DFS from root, assign values by the given formulas, store all in a hash set for O(1) membership. -![Tree recovery with value formulas](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Recover on every `find`, re-walking the tree — repeated O(n). -- **Optimized:** Recover once, store in a set — O(n) build, O(1) queries. +The problem asks us to recover a contaminated binary tree and then check if a target value exists. The tree follows a specific pattern: root is 0, and each node's value determines its children's values. -**1.4 Decomposition** -1. DFS from root with value parameter. -2. Assign `val`, insert into set. -3. Recurse to children with `2*val+1` and `2*val+2`. -4. `find` checks membership in the set. +**Brute force vs. optimized strategy** + +* **Brute Force:** For each find operation, traverse the entire tree to check if the target exists. This would be O(n) per find, which is inefficient for multiple queries. +* **Optimized:** Recover all values during initialization and store them in a hash set. Each find operation becomes O(1) lookup. This trades O(n) space for O(1) query time. + +**Decomposition** + +1. **Recovery:** Traverse the tree and recover each node's value based on its parent's value. +2. **Storage:** Store all recovered values in a hash set for O(1) lookup. +3. **Query:** Check if target exists in the hash set. ### Steps -**2.1 Initialization & Example Setup** -Start at root with value 0, empty set. +1. **Initialization & Example Setup** + Let's use a tree with root value -1 (contaminated) as our example. + - Initialize `self.values = set()` to store recovered values. + - Call `recover(root, 0)` to start recovery from root with value 0. -**2.2 Start Processing** -DFS visits each node, computing and storing values. +2. **Recovery Process** + The `recover` function recursively sets node values: + - If node is None, return (base case). + - Set `node.val = val` (recover the value). + - Add `val` to `self.values`. + - Recover left child: `recover(node.left, 2 * val + 1)`. + - Recover right child: `recover(node.right, 2 * val + 2)`. -**2.3 Trace Walkthrough** -| Node | Assigned val | Action | -|------|--------------|---------------| -| root | 0 | add to set | -| left | 1 | add to set | -| right| 2 | add to set | +3. **Trace Walkthrough** -**2.4 Increment and Loop** -Continue recursively; each node’s children follow the formula. +For a tree `[-1, null, -1]` (root with right child): +- Root: `recover(root, 0)` → `root.val = 0`, add 0 to set → `{0}` +- Right child: `recover(root.right, 2*0+2 = 2)` → `right.val = 2`, add 2 to set → `{0, 2}` -**2.5 Return Result** -`find(target)` returns `target in set`. +4. **Find Operation** + - `find(1)`: Check if 1 in `{0, 2}` → False + - `find(2)`: Check if 2 in `{0, 2}` → True +5. **Return Result** + Return True if target is in the set, False otherwise. diff --git a/explanations/1347/en.md b/explanations/1347/en.md index 61e8b53..e64599b 100644 --- a/explanations/1347/en.md +++ b/explanations/1347/en.md @@ -2,52 +2,47 @@ ### Strategy -**Restate the problem** -Given two equal-length lowercase strings `s` and `t`, find the minimum number of character replacements in `t` to make it an anagram of `s`. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** up to 5×10^4 characters. -- **Time Complexity:** O(n) to count frequencies. -- **Space Complexity:** O(1) since alphabet size is fixed (26). -- **Edge Case:** Already anagrams → 0 steps. +* **String Length:** Both strings have length 1 to 5*10^4, and they have the same length. They consist of lowercase English letters only. +* **Time Complexity:** We count frequencies in both strings (O(n)), then compare them (O(26) = O(1)). **Time Complexity: O(n)**, **Space Complexity: O(1)** for the frequency arrays (26 letters). +* **Edge Case:** If the strings are already anagrams, return 0. -**1.2 High-level approach** -Count letters in both strings; for each character, if `s` needs more than `t` has, that deficit contributes to the answer. -![Frequency gap guiding replacements](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Try all replacement combinations — exponential. -- **Optimized:** Frequency difference — O(n), straightforward. +The problem asks for the minimum steps to make string t an anagram of string s. In each step, we can replace any character in t. We need to count how many characters in t need to be changed to match the frequency of characters in s. -**1.4 Decomposition** -1. Count frequencies of `s` and `t`. -2. For each letter, compute `max(0, count_s - count_t)` and sum. -3. That sum is the number of replacements needed in `t`. -4. Return the sum. +**Brute force vs. optimized strategy** + +* **Brute Force:** Try all possible character replacements. This would be exponential. +* **Optimized:** Count character frequencies in both strings. For each character, if s has more occurrences than t, we need to replace (count_s[char] - count_t[char]) characters in t. + +**Decomposition** + +1. **Count Frequencies:** Count occurrences of each character in s and t. +2. **Calculate Differences:** For each character, if s has more occurrences, add the difference to the result. +3. **Return:** The total number of replacements needed. ### Steps -**2.1 Initialization & Example Setup** -Example: `s="leetcode"`, `t="practice"`. +1. **Initialization & Example Setup** + Let's use `s = "bab"`, `t = "aba"` as our example. + - Count s: `count_s = {'b': 2, 'a': 1}`. + - Count t: `count_t = {'a': 2, 'b': 1}`. -**2.2 Start Checking** -Build `count_s`, `count_t`. +2. **Calculate Differences** + - For 'a': `count_s['a'] = 1`, `count_t['a'] = 2`. No change needed (t has enough). + - For 'b': `count_s['b'] = 2`, `count_t['b'] = 1`. Need `2 - 1 = 1` replacement. -**2.3 Trace Walkthrough** -| Char | count_s | count_t | deficit (if s needs more) | -|------|---------|---------|----------------------------| -| e | 3 | 1 | +2 | -| l | 1 | 0 | +1 | -| t | 1 | 1 | 0 | -| c | 1 | 2 | 0 | -| o | 1 | 0 | +1 | -| d | 1 | 0 | +1 | -| ... | ... | ... | ... | -Total = 5. +3. **Trace Walkthrough** -**2.4 Increment and Loop** -Sum deficits over all 26 letters. +| Character | count_s | count_t | Difference | Steps Needed | +|-----------|---------|---------|------------|--------------| +| 'a' | 1 | 2 | 1 - 2 = -1 | 0 (t has enough) | +| 'b' | 2 | 1 | 2 - 1 = 1 | 1 | -**2.5 Return Result** -Return the total replacements (5 in the example). +4. **Result** + Total steps = 1. We need to replace one 'a' in t with 'b' to get "bba" or "abb", which are anagrams of "bab". +5. **Return Result** + Return the total number of steps: 1. diff --git a/explanations/1551/en.md b/explanations/1551/en.md index 4c0aec1..4694b9e 100644 --- a/explanations/1551/en.md +++ b/explanations/1551/en.md @@ -2,45 +2,49 @@ ### Strategy -**Restate the problem** -Array is `[1,3,5,...,2n-1]`; in one operation, decrement one element and increment another. Find min operations to make all elements equal. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= n <= 1e4`. -- **Time Complexity:** O(n) to sum needed moves. -- **Space Complexity:** O(1). -- **Edge Case:** n=1 → 0 operations. +* **Array Length:** n can be 1 to 10^4. The array is defined as `arr[i] = 2*i + 1`, so it contains odd numbers: [1, 3, 5, 7, ...]. +* **Time Complexity:** We iterate through at most n/2 elements to calculate operations. **Time Complexity: O(n)**, **Space Complexity: O(1)**. +* **Edge Case:** If n=1, the array is already equal ([1]), so operations = 0. -**1.2 High-level approach** -Target value is `n` (the average). Pair symmetric elements; each pair needs `(target - current_lower)` moves. Sum over first half. -![Balancing around the center value](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Simulate operations — inefficient. -- **Optimized:** Direct formula using symmetry — O(n). +The problem asks for the minimum operations to make all array elements equal. Since we can only transfer 1 from one element to another, the target value must be the average of all elements. For an array of odd numbers starting from 1, the average is n. -**1.4 Decomposition** -1. Target = n. -2. For i in [0 .. n/2 - 1], current = 2*i+1. -3. Add `target - current` to result. -4. Return result. +**Brute force vs. optimized strategy** + +* **Brute Force:** Simulate the operations step by step. This would be very slow. +* **Optimized:** Calculate the target value (which is n), then for each element less than target, calculate how many operations are needed to bring it to target. Since elements are symmetric, we only need to process half the array. + +**Decomposition** + +1. **Calculate Target:** The target value is n (the average). +2. **Count Operations:** For each element less than target, add (target - element) to the result. +3. **Return:** Since operations are symmetric, we can stop at the middle. ### Steps -**2.1 Initialization & Example Setup** -Example: n=3; array [1,3,5], target=3. +1. **Initialization & Example Setup** + Let's use `n = 3` as our example. + - Array: `arr = [1, 3, 5]` (indices 0, 1, 2). + - Target: `target = n = 3`. -**2.2 Start Checking** -Only i=0 in first half: current=1 → need 2 moves. +2. **Calculate Operations** + - Element at index 0: `current = 2*0 + 1 = 1`, needs `3 - 1 = 2` operations. + - Element at index 1: `current = 2*1 + 1 = 3`, already equal to target. + - We can stop here since elements are symmetric. -**2.3 Trace Walkthrough** -| i | current | target | add to res | -|---|---------|--------|------------| -| 0 | 1 | 3 | 2 | +3. **Trace Walkthrough** -**2.4 Increment and Loop** -Loop over first half (integer division). +| Index i | current = 2*i+1 | target | Operations Needed | Running Total | +|---------|-----------------|--------|-------------------|---------------| +| 0 | 1 | 3 | 3 - 1 = 2 | 2 | +| 1 | 3 | 3 | 0 (stop) | 2 | -**2.5 Return Result** -Return accumulated moves (2 for n=3). +4. **Mathematical Insight** + For n=3: operations = (3-1) = 2. + For n=6: operations = (6-1) + (6-3) + (6-5) = 5 + 3 + 1 = 9. +5. **Return Result** + Return the total operations: 2 for n=3. diff --git a/explanations/1605/en.md b/explanations/1605/en.md index 12a947f..d1a3750 100644 --- a/explanations/1605/en.md +++ b/explanations/1605/en.md @@ -2,50 +2,56 @@ ### Strategy -**Restate the problem** -Construct a non-negative matrix that matches given row sums and column sums. - -**1.1 Constraints & Complexity** -- **Input Size:** up to 500 rows/cols. -- **Time Complexity:** O(m*n) greedy fill. -- **Space Complexity:** O(m*n) for the output matrix. -- **Edge Case:** Trivial 1x1 matrix equals the shared sum. - -**1.2 High-level approach** -Greedy: at cell (i,j), place `min(rowSum[i], colSum[j])`, then decrement both sums; proceed row by row. -![Greedy fill consuming row/col budgets](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) - -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Search all matrices satisfying sums — exponential. -- **Optimized:** Greedy works because any feasible solution can allocate the minimal remaining budget without violating totals. - -**1.4 Decomposition** -1. Initialize `res` with zeros. -2. For each cell `(i,j)`: - - `val = min(rowSum[i], colSum[j])` - - Set `res[i][j] = val`, update rowSum/colSum. -3. Continue until all cells processed. -4. Return `res`. +**Constraints & Edge Cases** -### Steps +* **Matrix Size:** rowSum and colSum have length 1-500, with values up to 10^8. The sum of rowSum equals sum of colSum (guaranteed). +* **Time Complexity:** We iterate through each cell once, taking the minimum of rowSum[i] and colSum[j]. **Time Complexity: O(m * n)**, **Space Complexity: O(m * n)** for the result matrix. +* **Edge Case:** If all row and column sums are 0, return a zero matrix. + +**High-level approach** -**2.1 Initialization & Example Setup** -Example: `rowSum=[3,8]`, `colSum=[4,7]`, res all zeros. +The problem asks us to construct a matrix given row and column sums. We use a greedy approach: for each cell, place the minimum of the remaining row sum and column sum, then update both. -**2.2 Start Checking** -At (0,0): min(3,4)=3 → res[0][0]=3; rowSum=[0,8], colSum=[1,7]. +**Brute force vs. optimized strategy** -**2.3 Trace Walkthrough** -| Cell | rowSum before | colSum before | placed | rowSum after | colSum after | -|------|---------------|---------------|--------|--------------|--------------| -| (0,0)| 3 | 4 | 3 | 0 | 1 | -| (0,1)| 0 | 7 | 0 | 0 | 7 | -| (1,0)| 8 | 1 | 1 | 7 | 0 | -| (1,1)| 7 | 7 | 7 | 0 | 0 | +* **Brute Force:** Try all possible matrix configurations and check if they satisfy the constraints. This would be exponential. +* **Optimized:** Greedy approach - for each cell (i,j), place `min(rowSum[i], colSum[j])`, then subtract this value from both rowSum[i] and colSum[j]. This ensures we always make progress and never exceed constraints. -**2.4 Increment and Loop** -Proceed left-to-right, top-to-bottom. +**Decomposition** -**2.5 Return Result** -Matrix satisfies all row/column sums. +1. **Initialize Matrix:** Create an m x n matrix filled with zeros. +2. **Greedy Placement:** For each cell (i,j), place the minimum of rowSum[i] and colSum[j]. +3. **Update Sums:** Subtract the placed value from both rowSum[i] and colSum[j]. + +### Steps +1. **Initialization & Example Setup** + Let's use `rowSum = [3,8]`, `colSum = [4,7]` as our example. + - Initialize `res = [[0,0],[0,0]]`. + - m = 2, n = 2. + +2. **Process Cells** + For cell (0,0): + - `val = min(rowSum[0], colSum[0]) = min(3, 4) = 3`. + - `res[0][0] = 3`. + - `rowSum[0] -= 3` → `rowSum = [0, 8]`. + - `colSum[0] -= 3` → `colSum = [1, 7]`. + +3. **Trace Walkthrough** + +| Cell (i,j) | rowSum[i] | colSum[j] | val = min(...) | res[i][j] | Updated rowSum | Updated colSum | +|------------|-----------|-----------|----------------|-----------|----------------|----------------| +| (0,0) | 3 | 4 | 3 | 3 | [0, 8] | [1, 7] | +| (0,1) | 0 | 7 | 0 | 0 | [0, 8] | [1, 7] | +| (1,0) | 8 | 1 | 1 | 1 | [0, 7] | [0, 7] | +| (1,1) | 7 | 7 | 7 | 7 | [0, 0] | [0, 0] | + +4. **Result Matrix** + `res = [[3,0],[1,7]]` + - Row 0: 3 + 0 = 3 ✓ + - Row 1: 1 + 7 = 8 ✓ + - Col 0: 3 + 1 = 4 ✓ + - Col 1: 0 + 7 = 7 ✓ + +5. **Return Result** + Return the constructed matrix. diff --git a/explanations/1630/en.md b/explanations/1630/en.md index e4b5da6..f5d511f 100644 --- a/explanations/1630/en.md +++ b/explanations/1630/en.md @@ -2,46 +2,51 @@ ### Strategy -**Restate the problem** -For each query `[l_i, r_i]`, decide if the subarray can be rearranged into an arithmetic progression. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `n, m <= 500`. -- **Time Complexity:** O(m * k log k), where k is subarray length (sort each subarray). -- **Space Complexity:** O(k) per query for the sorted copy. -- **Edge Case:** Subarray of length 2 is always arithmetic. +* **Array Sizes:** nums has length 2-500, and we have 1-500 range queries. Each query specifies a subarray that can be rearranged. +* **Time Complexity:** For each query, we extract the subarray (O(n)), sort it (O(n log n)), and check if it's arithmetic (O(n)). With m queries, total is O(m * n log n). **Time Complexity: O(m * n log n)**, **Space Complexity: O(n)** for the sorted subarray. +* **Edge Case:** If a subarray has less than 2 elements, it's trivially arithmetic (return True). -**1.2 High-level approach** -Extract subarray, sort it, ensure consecutive differences are equal. -![Sorted subarray check](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Try all permutations — factorial blowup. -- **Optimized:** Sorting then one pass diff check — O(k log k). +The problem asks us to check if subarrays can be rearranged to form arithmetic sequences. A sequence is arithmetic if the difference between consecutive elements is constant. -**1.4 Decomposition** -1. For each query, copy subarray `nums[l:r+1]`. -2. Sort it. -3. Compute common diff = `sorted[1]-sorted[0]`. -4. Verify all consecutive diffs match; if any differ, mark false; else true. +**Brute force vs. optimized strategy** + +* **Brute Force:** For each query, try all permutations of the subarray to see if any forms an arithmetic sequence. This would be O(n! * n) per query, which is exponential. +* **Optimized:** Sort the subarray and check if the sorted version is arithmetic. If a set of numbers can form an arithmetic sequence, its sorted version will be arithmetic. This is O(n log n) per query. + +**Decomposition** + +1. **Extract Subarray:** Get the subarray from nums[l[i]] to nums[r[i]]. +2. **Sort:** Sort the subarray to check if it can be arithmetic. +3. **Check Arithmetic:** Verify that differences between consecutive elements are constant. ### Steps -**2.1 Initialization & Example Setup** -Example: `nums=[4,6,5,9,3,7]`, query `[0,2]` → subarray `[4,6,5]`. +1. **Initialization & Example Setup** + Let's use `nums = [4,6,5,9,3,7]`, `l = [0,0,2]`, `r = [2,3,5]` as our example. + - Initialize `res = []` to store results. -**2.2 Start Checking** -Sort → `[4,5,6]`, diff = 1. +2. **Process Each Query** + For query i = 0: `l[0]=0`, `r[0]=2` + - Extract subarray: `nums[0:3] = [4,6,5]` + - Sort: `[4,5,6]` + - Check differences: `5-4=1`, `6-5=1` → constant difference → True -**2.3 Trace Walkthrough** -| Subarray (sorted) | Step | Diff check | OK? | -|-------------------|------|----------------------|-----| -| [4,5,6] | 4→5 | 1 == diff | ✓ | -| | 5→6 | 1 == diff | ✓ | +3. **Trace Walkthrough** -**2.4 Increment and Loop** -Repeat for each query independently. +| Query | l[i] | r[i] | Subarray | Sorted | Differences | Result | +|-------|------|------|----------|--------|-------------|--------| +| 0 | 0 | 2 | [4,6,5] | [4,5,6] | 1, 1 | True | +| 1 | 0 | 3 | [4,6,5,9]| [4,5,6,9]| 1, 1, 3 | False | +| 2 | 2 | 5 | [5,9,3,7]| [3,5,7,9]| 2, 2, 2 | True | -**2.5 Return Result** -Append boolean for each query into `res`. +4. **Check Arithmetic Logic** + - If sorted array has less than 2 elements, return True. + - Calculate `diff = sorted[1] - sorted[0]`. + - For each subsequent pair, check if `sorted[i] - sorted[i-1] == diff`. +5. **Return Result** + Append True or False to `res` for each query, then return `res`. diff --git a/explanations/1823/en.md b/explanations/1823/en.md index 13cd293..48ed58d 100644 --- a/explanations/1823/en.md +++ b/explanations/1823/en.md @@ -2,47 +2,52 @@ ### Strategy -**Restate the problem** -`n` players in a circle, eliminate every `k`th; return the winner’s label (1-indexed). +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= k <= n <= 500`. -- **Time Complexity:** O(n) using the Josephus recurrence. -- **Space Complexity:** O(1). -- **Edge Case:** n=1 → winner=1. +* **Game Parameters:** n (friends) and k (counting) are both 1-500. This is the classic Josephus problem. +* **Time Complexity:** We use the Josephus recurrence formula which processes each friend once. **Time Complexity: O(n)**, **Space Complexity: O(1)**. +* **Edge Case:** If n=1, there's only one friend, so they win (return 1). -**1.2 High-level approach** -Use the Josephus recurrence: winner (0-indexed) updates as `winner = (winner + k) % i` for i from 2..n; convert to 1-indexed at the end. -![Josephus circular elimination](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Simulate with a queue/list and remove elements — O(n*k). -- **Optimized:** Closed-form recurrence — O(n). +The problem asks us to find the winner in a circular elimination game. This is the classic Josephus problem. We use the mathematical recurrence: `J(n,k) = (J(n-1,k) + k) % n`, with base case `J(1,k) = 0` (0-indexed), then convert to 1-indexed. -**1.4 Decomposition** -1. Set `winner = 0` (for i=1). -2. For `i` from 2 to `n`: `winner = (winner + k) % i`. -3. Return `winner + 1`. +**Brute force vs. optimized strategy** + +* **Brute Force:** Simulate the game step by step, removing friends in order. This would be O(n*k) time. +* **Optimized:** Use the Josephus recurrence formula to calculate the winner directly in O(n) time. + +**Decomposition** + +1. **Base Case:** For 1 friend, the winner is friend 0 (0-indexed) or friend 1 (1-indexed). +2. **Recurrence:** For n friends, the winner is `(winner(n-1) + k) % n`. +3. **Convert to 1-indexed:** Add 1 to the 0-indexed result. ### Steps -**2.1 Initialization & Example Setup** -Example: `n=5, k=2`; start `winner=0`. +1. **Initialization & Example Setup** + Let's use `n = 5`, `k = 2` as our example. + - We'll work in 0-indexed (0,1,2,3,4) and convert to 1-indexed at the end. -**2.2 Start Checking** -Iterate i=2..5 updating winner. +2. **Josephus Recurrence** + - `J(1,2) = 0` (base case: 1 friend, winner is 0). + - `J(2,2) = (J(1,2) + 2) % 2 = (0 + 2) % 2 = 0`. + - `J(3,2) = (J(2,2) + 2) % 3 = (0 + 2) % 3 = 2`. + - `J(4,2) = (J(3,2) + 2) % 4 = (2 + 2) % 4 = 0`. + - `J(5,2) = (J(4,2) + 2) % 5 = (0 + 2) % 5 = 2`. -**2.3 Trace Walkthrough** -| i | winner before | update formula | winner after | -|---|---------------|----------------------|--------------| -| 2 | 0 | (0+2)%2 = 0 | 0 | -| 3 | 0 | (0+2)%3 = 2 | 2 | -| 4 | 2 | (2+2)%4 = 0 | 0 | -| 5 | 0 | (0+2)%5 = 2 | 2 | +3. **Trace Walkthrough** -**2.4 Increment and Loop** -Loop until i = n. +| n | J(n,2) Calculation | Result (0-indexed) | +|---|-------------------|-------------------| +| 1 | 0 (base case) | 0 | +| 2 | (0 + 2) % 2 | 0 | +| 3 | (0 + 2) % 3 | 2 | +| 4 | (2 + 2) % 4 | 0 | +| 5 | (0 + 2) % 5 | 2 | -**2.5 Return Result** -Return `winner + 1` → 3 for the example. +4. **Convert to 1-indexed** + - `J(5,2) = 2` (0-indexed) → `2 + 1 = 3` (1-indexed). +5. **Return Result** + Return 3, which is the winner in 1-indexed. diff --git a/explanations/2120/en.md b/explanations/2120/en.md index c31c35d..8ecb184 100644 --- a/explanations/2120/en.md +++ b/explanations/2120/en.md @@ -2,50 +2,56 @@ ### Strategy -**Restate the problem** -Given grid size `n`, start position, and instructions `s`, for each suffix of `s` compute how many moves stay within the grid. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= n, m <= 500`, m = |s|. -- **Time Complexity:** O(m²) in the straightforward simulation (m up to 500 is fine). -- **Space Complexity:** O(1) extra besides output. -- **Edge Case:** Single instruction that immediately leaves → 0 for that suffix. +* **Grid and Instructions:** n and m (instruction length) are 1-500. The robot starts at startPos and executes instructions. +* **Time Complexity:** For each starting position i, we simulate execution until the robot goes out of bounds. Worst case is O(m²). **Time Complexity: O(m²)**, **Space Complexity: O(m)** for the result array. +* **Edge Case:** If the robot would go out of bounds on the first instruction, return 0 for that position. -**1.2 High-level approach** -For each starting index i, simulate moves from that suffix until leaving bounds. -![Suffix simulation on grid](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force / Direct:** Simulate each suffix independently — O(m²). Acceptable for m=500. -- **Optimized:** Could precompute deltas, but unnecessary here. +The problem asks us to find, for each starting position i in the instruction string, how many instructions the robot can execute before going out of bounds. We simulate the execution for each starting position. -**1.4 Decomposition** -1. Map instructions to direction deltas. -2. For each i in [0..m-1], start from `startPos`, step through `s[i:]` until out of bounds. -3. Count steps that remain in bounds. -4. Store counts in `res`. +**Brute force vs. optimized strategy** -### Steps - -**2.1 Initialization & Example Setup** -Example: `n=3, start=[0,1], s="RRDDLU"`, m=6. +* **Brute Force:** This is what we do - simulate execution for each starting position. There's no more efficient way without complex preprocessing. +* **Optimized:** We could potentially use dynamic programming or prefix sums, but simulation is straightforward and efficient enough for the constraints. -**2.2 Start Checking** -For each suffix, reset position and simulate. +**Decomposition** -**2.3 Trace Walkthrough** -| start i | suffix | path validity count | -|---------|----------|---------------------| -| 0 | RRDDLU | 1 | -| 1 | RDDLU | 5 | -| 2 | DDLU | 4 | -| 3 | DLU | 3 | -| 4 | LU | 1 | -| 5 | U | 0 | +1. **For Each Start Position:** Try starting from each index i in the instruction string. +2. **Simulate Execution:** Execute instructions one by one, updating robot position. +3. **Count Instructions:** Count how many instructions can be executed before going out of bounds. +4. **Store Result:** Save the count for each starting position. -**2.4 Increment and Loop** -Repeat for all suffix starts. - -**2.5 Return Result** -Return the array of counts, e.g., `[1,5,4,3,1,0]`. +### Steps +1. **Initialization & Example Setup** + Let's use `n = 3`, `startPos = [0,1]`, `s = "RRDDLU"` as our example. + - Initialize `res = []`. + +2. **Process Each Starting Position** + For i = 0: Start at (0,1), execute from "RRDDLU" + - 'R': (0,1) → (0,2) ✓ (within bounds) + - 'R': (0,2) → (0,3) ✗ (out of bounds, col=3 >= n=3) + - Count = 1 + +3. **Trace Walkthrough** + +| Start i | Instructions | Execution | Position After | In Bounds? | Count | +|---------|--------------|-----------|----------------|------------|-------| +| 0 | "RRDDLU" | R | (0,2) | Yes | 1 | +| 0 | "RRDDLU" | R | (0,3) | No | Stop | +| 1 | "RDDLU" | R | (0,2) | Yes | 1 | +| 1 | "RDDLU" | D | (1,2) | Yes | 2 | +| 1 | "RDDLU" | D | (2,2) | Yes | 3 | +| 1 | "RDDLU" | L | (2,1) | Yes | 4 | +| 1 | "RDDLU" | U | (1,1) | Yes | 5 | + +4. **Simulation Logic** + - For each instruction: 'L'→col-1, 'R'→col+1, 'U'→row-1, 'D'→row+1 + - Check if new position is within [0, n-1] for both row and col + - If out of bounds, stop and record count + +5. **Return Result** + Return `res = [1,5,4,3,1,0]` for all starting positions. diff --git a/explanations/2221/en.md b/explanations/2221/en.md index 0eacf26..023aaa2 100644 --- a/explanations/2221/en.md +++ b/explanations/2221/en.md @@ -2,52 +2,49 @@ ### Strategy -**Restate the problem** -Repeatedly replace the array with pairwise sums mod 10 until one element remains; return it. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= n <= 1000`. -- **Time Complexity:** O(n²) in the direct simulation (each layer shrinks by 1). -- **Space Complexity:** O(n) for the working array. -- **Edge Case:** n=1 → return the single value. +* **Array Size:** nums has length 1-1000, with values 0-9. We repeatedly reduce the array until only one element remains. +* **Time Complexity:** We perform n-1 iterations, and each iteration processes the current array. Total operations are roughly O(n²). **Time Complexity: O(n²)**, **Space Complexity: O(n)** for the new array. +* **Edge Case:** If nums has only one element, return that element directly. -**1.2 High-level approach** -Simulate the reduction: build a new array of length-1 from `(nums[i]+nums[i+1]) % 10` until length=1. -![Layered triangular reduction](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Same as direct simulation — O(n²), acceptable for n=1000. -- **Optimized:** Binomial-coefficient mod 10 approach exists, but simulation suffices. +The problem asks us to repeatedly create a new array where each element is `(nums[i] + nums[i+1]) % 10`, until only one element remains. This is similar to computing a triangular sum. -**1.4 Decomposition** -1. While `len(nums) > 1`: - - Build `newNums` of size `len(nums)-1` with `(nums[i]+nums[i+1])%10`. - - Assign `nums = newNums`. -2. Return `nums[0]`. +**Brute force vs. optimized strategy** + +* **Brute Force:** This is what we do - simulate the process step by step. There's no more efficient way for this problem. +* **Optimized:** We can optimize space by reusing the array, but the time complexity remains the same. + +**Decomposition** + +1. **Iterate:** While array length > 1, create a new array. +2. **Compute Pairs:** For each adjacent pair, compute `(nums[i] + nums[i+1]) % 10`. +3. **Update:** Replace nums with the new array. +4. **Return:** When length is 1, return that element. ### Steps -**2.1 Initialization & Example Setup** -Example: `nums = [1,2,3,4,5]`. +1. **Initialization & Example Setup** + Let's use `nums = [1,2,3,4,5]` as our example. -**2.2 Start Checking** -Layer 1: `[3,5,7,9]` → mod 10: `[3,5,7,9]` -Layer 2: `[8,2,6]` -Layer 3: `[0,8]` -Layer 4: `[8]` +2. **First Iteration** + - Create new array: `[(1+2)%10, (2+3)%10, (3+4)%10, (4+5)%10]` + - `= [3, 5, 7, 9]` + - Update: `nums = [3,5,7,9]` -**2.3 Trace Walkthrough** -| Layer | Array state | -|-------|----------------| -| 0 | [1,2,3,4,5] | -| 1 | [3,5,7,9] | -| 2 | [8,2,6] | -| 3 | [0,8] | -| 4 | [8] | +3. **Trace Walkthrough** -**2.4 Increment and Loop** -Repeat until a single value remains. +| Iteration | nums | New Array Calculation | Result | +|-----------|------|----------------------|--------| +| 0 | [1,2,3,4,5] | [(1+2)%10, (2+3)%10, (3+4)%10, (4+5)%10] | [3,5,7,9] | +| 1 | [3,5,7,9] | [(3+5)%10, (5+7)%10, (7+9)%10] | [8,2,6] | +| 2 | [8,2,6] | [(8+2)%10, (2+6)%10] | [0,8] | +| 3 | [0,8] | [(0+8)%10] | [8] | -**2.5 Return Result** -Return the last value (8 in the example). +4. **Continue Until One Element** + - Keep iterating until `len(nums) == 1`. +5. **Return Result** + Return `nums[0] = 8`. diff --git a/explanations/2326/en.md b/explanations/2326/en.md index 0e42be8..0564355 100644 --- a/explanations/2326/en.md +++ b/explanations/2326/en.md @@ -2,48 +2,58 @@ ### Strategy -**Restate the problem** -Fill an `m x n` matrix in spiral order using values from a linked list; remaining cells become -1. - -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= m*n <= 1e5`; list length up to `m*n`. -- **Time Complexity:** O(m*n) to visit each cell once. -- **Space Complexity:** O(1) extra (matrix output not counted). -- **Edge Case:** List shorter than cells → trailing -1s. - -**1.2 High-level approach** -Track boundaries/directions for spiral traversal, place list values until exhausted, then fill -1 stays. -![Spiral traversal boundaries](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) - -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Recompute visited checks with sets — higher overhead. -- **Optimized:** Direction vectors with boundary checks — O(1) per move. - -**1.4 Decomposition** -1. Initialize matrix with -1. -2. Set direction order: right, down, left, up. -3. Walk cell by cell, placing list values. -4. When the next cell is out-of-bounds or already filled, turn clockwise. -5. Continue until list ends or all cells visited. +**Constraints & Edge Cases** -### Steps +* **Matrix Size:** m and n can be up to 10^5, but m*n ≤ 10^5. The linked list has 1 to m*n nodes. +* **Time Complexity:** We traverse the linked list once and fill the matrix in spiral order. **Time Complexity: O(m * n)**, **Space Complexity: O(m * n)** for the result matrix. +* **Edge Case:** If the linked list has fewer nodes than m*n, remaining cells are filled with -1. + +**High-level approach** -**2.1 Initialization & Example Setup** -Example: `m=3, n=5`, list `[3,0,2,6,8,1,7,9,4,2,5,5,0]`; start at `(0,0)`, dir=right. +The problem asks us to fill an m x n matrix in spiral (clockwise) order starting from top-left, using values from a linked list. We use direction vectors to navigate the spiral pattern. -**2.2 Start Checking** -Place value, attempt next move; if blocked, rotate direction. +**Brute force vs. optimized strategy** -**2.3 Trace Walkthrough** -| Step | Pos (r,c) | Dir | Value placed | Next move valid? | -|------|-----------|--------|--------------|------------------| -| 1 | (0,0) | right | 3 | yes | -| ... | ... | ... | ... | ... | -| turn | boundary | rotate | — | — | +* **Brute Force:** This is essentially what we do - traverse in spiral order. There's no more efficient way. +* **Optimized:** Use direction vectors [(0,1), (1,0), (0,-1), (-1,0)] to represent right, down, left, up. When we hit a boundary or visited cell, rotate direction. -**2.4 Increment and Loop** -Advance through list nodes; rotate as needed until list ends. +**Decomposition** -**2.5 Return Result** -Matrix filled in spiral with remaining cells as -1. +1. **Initialize Matrix:** Create m x n matrix filled with -1. +2. **Spiral Traversal:** Use direction vectors to move in spiral pattern. +3. **Direction Change:** When hitting boundary or visited cell, rotate to next direction. +4. **Fill Values:** Place linked list values as we traverse. + +### Steps +1. **Initialization & Example Setup** + Let's use `m = 3`, `n = 5`, `head = [3,0,2,6,8,1,7,9,4,2,5,5,0]` as our example. + - Initialize `res = [[-1]*5 for _ in range(3)]`. + - Directions: `[(0,1), (1,0), (0,-1), (-1,0)]` (right, down, left, up). + - Start at `(0,0)` with `dir_idx = 0` (right). + +2. **Spiral Traversal** + - Place head.val = 3 at (0,0), move right. + - Continue placing values and moving in current direction. + - When hitting boundary or -1 (visited), change direction. + +3. **Trace Walkthrough** + +| Step | Position | Value | Direction | Next Position | Action | +|------|----------|-------|-----------|---------------|--------| +| 1 | (0,0) | 3 | Right | (0,1) | Place 3, move right | +| 2 | (0,1) | 0 | Right | (0,2) | Place 0, move right | +| 3 | (0,2) | 2 | Right | (0,3) | Place 2, move right | +| 4 | (0,3) | 6 | Right | (0,4) | Place 6, move right | +| 5 | (0,4) | 8 | Right | (0,5) - out | Place 8, change to down | +| 6 | (1,4) | 1 | Down | (2,4) | Place 1, move down | +| 7 | (2,4) | 7 | Down | (3,4) - out | Place 7, change to left | +| 8 | (2,3) | 9 | Left | (2,2) | Place 9, move left | +| ... | ... | ... | ... | ... | Continue spiral | + +4. **Direction Change Logic** + - Check if next position is out of bounds or already filled (value != -1). + - If yes, rotate direction: `dir_idx = (dir_idx + 1) % 4`. + +5. **Return Result** + Return the filled matrix with remaining cells as -1. diff --git a/explanations/237/en.md b/explanations/237/en.md index c6426c7..8fd452e 100644 --- a/explanations/237/en.md +++ b/explanations/237/en.md @@ -2,104 +2,48 @@ ### Strategy -**Restate the problem** -Delete a given node (not tail) from a singly linked list when only that node reference is provided. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** 2 to 1000 nodes. -- **Time Complexity:** O(1) for the delete operation. -- **Space Complexity:** O(1). -- **Edge Case:** The given node’s next exists (guaranteed). +* **Linked List:** The list has 2-1000 nodes with unique values. The node to delete is guaranteed to not be the tail node. +* **Time Complexity:** We only need to copy the next node's value and update the pointer, which is O(1). **Time Complexity: O(1)**, **Space Complexity: O(1)**. +* **Edge Case:** Since the node is guaranteed not to be the tail, we can always access `node.next`. -**1.2 High-level approach** -Copy the next node’s value into the current node, then bypass the next node. -![In-place delete by copying next](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Traverse from head to find previous node — impossible without head. -- **Optimized:** Overwrite current node with next node data; O(1). +The problem asks us to delete a node from a linked list, but we're only given access to that node (not the head). We can't delete the node itself, but we can make it "disappear" by copying the next node's data and skipping the next node. -**1.4 Decomposition** -1. Set `node.val = node.next.val`. -2. Link `node.next = node.next.next`. -3. Done. +**Brute force vs. optimized strategy** -### Steps - -**2.1 Initialization & Example Setup** -List: `4 -> 5 -> 1 -> 9`, node to delete = value 5. - -**2.2 Start Checking** -Copy next (1) into current. - -**2.3 Trace Walkthrough** -| Step | node.val before | node.next.val | Action | List becomes | -|------|-----------------|---------------|-----------------------------|---------------------| -| 1 | 5 | 1 | node.val = 1 | 4 -> 1 -> 1 -> 9 | -| 2 | 1 | 1 | node.next = node.next.next | 4 -> 1 -> 9 | - -**2.4 Increment and Loop** -No loop; constant steps. - -**2.5 Return Result** -Node effectively removed; list is `4 -> 1 -> 9`. -## Explanation - -### Strategy - -**Restate the problem** - -We need to delete a node from a linked list, but we are only given access to the node to be deleted (not the head). We cannot access the previous node directly. - -**1.1 Constraints & Complexity** +* **Brute Force:** There's no brute force needed here - we simply copy the next node's value and update the pointer. +* **Optimized:** Copy `node.next.val` to `node.val`, then set `node.next = node.next.next`. This effectively removes the current node from the list. -- **Input Size:** The linked list has 2 to 1000 nodes. -- **Time Complexity:** O(1) - We only perform a constant number of operations. -- **Space Complexity:** O(1) - No additional space is used. -- **Edge Case:** The node to delete is guaranteed not to be the tail node, so we can safely access node.next.next. +**Decomposition** -**1.2 High-level approach** - -Since we cannot access the previous node, we cannot directly unlink the current node. Instead, we copy the value from the next node into the current node, then skip the next node by linking the current node to the node after the next one. - -![Linked list node deletion by copying value](https://assets.leetcode.com/uploads/2020/09/01/node1.jpg) - -**1.3 Brute force vs. optimized strategy** - -- **Brute Force:** If we had access to the head, we could traverse to find the previous node and update its next pointer. This would be O(n) time. -- **Optimized Strategy:** Copy the next node's value to the current node and skip the next node. This is O(1) time. -- **Why optimized is better:** We achieve constant time deletion by cleverly copying data instead of restructuring the list. - -**1.4 Decomposition** - -1. **Copy Value:** Copy the value from node.next to node. -2. **Skip Next Node:** Update node.next to point to node.next.next, effectively removing the next node from the list. +1. **Copy Value:** Copy the next node's value to the current node. +2. **Skip Next Node:** Update the current node's next pointer to skip the next node. ### Steps -**2.1 Initialization & Example Setup** - -Let's use the example: `head = [4,5,1,9]`, `node = 5` (the node with value 5) - -- The linked list: 4 → 5 → 1 → 9 -- We are given the node containing value 5 (second node) - -**2.2 Start Processing** - -We need to delete the node with value 5. Since we only have access to this node, we cannot modify the previous node (4). - -**2.3 Trace Walkthrough** +1. **Initialization & Example Setup** + Let's use `head = [4,5,1,9]`, `node = 5` (the node with value 5) as our example. + - The node to delete is the second node (value 5). + - We have: `4 -> 5 -> 1 -> 9`. -| Step | Current Node Value | Next Node Value | Action | Result | -|------|-------------------|-----------------|--------|--------| -| Initial | 5 | 1 | - | 4 → 5 → 1 → 9 | -| Copy | 5 → 1 | 1 | Copy next value | 4 → 1 → 1 → 9 | -| Skip | 1 | 9 | Link to next.next | 4 → 1 → 9 | +2. **Copy Next Node's Value** + - `node.val = node.next.val` → `node.val = 1`. + - Now the list looks like: `4 -> 1 -> 1 -> 9` (temporarily has duplicate 1). -**2.4 Update Pointers** +3. **Skip Next Node** + - `node.next = node.next.next` → Skip the node with value 1. + - Now: `4 -> 1 -> 9`. -After copying the value, we update `node.next = node.next.next`, which removes the duplicate node from the list. +4. **Trace Walkthrough** -**2.5 Return Result** +| Step | Action | List State | +|------|--------|------------| +| Initial | - | 4 -> 5 -> 1 -> 9 | +| 1 | Copy next.val to node.val | 4 -> 1 -> 1 -> 9 | +| 2 | Skip next node | 4 -> 1 -> 9 | -The list becomes `[4,1,9]`, effectively deleting the original node with value 5. Note that we actually deleted the next node, but from the user's perspective, the node with value 5 is gone. +5. **Result** + The node with value 5 is effectively removed. The list now contains [4,1,9]. diff --git a/explanations/2482/en.md b/explanations/2482/en.md index 93db35d..f1aa26d 100644 --- a/explanations/2482/en.md +++ b/explanations/2482/en.md @@ -2,122 +2,68 @@ ### Strategy -**Restate the problem** -For each cell `(i,j)` in a binary matrix, compute `onesRow[i] + onesCol[j] - zerosRow[i] - zerosCol[j]`. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= m*n <= 1e5`. -- **Time Complexity:** O(m*n) to count and build output. -- **Space Complexity:** O(m + n) for row/col counts. -- **Edge Case:** Single cell matrix. +* **Matrix Size:** The input is an m x n binary matrix where m and n can be up to 10^5, but m * n is at most 10^5. This means the matrix could be very wide or very tall. +* **Time Complexity:** We need O(m * n) time to process all elements, which is optimal since we must examine each cell at least once. **Time Complexity: O(m * n)**, **Space Complexity: O(m + n)** for storing row and column counts. +* **Edge Case:** If the matrix is all zeros or all ones, the calculation still works correctly. -**1.2 High-level approach** -Precompute ones per row and column, derive zeros from lengths, then fill the answer. -![Row/column accumulation](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** For each cell, scan its row and column — O(m*n*(m+n)). -- **Optimized:** Precompute counts once — O(m*n). +The problem asks us to create a difference matrix where each cell `diff[i][j]` equals `onesRow[i] + onesCol[j] - zerosRow[i] - zerosCol[j]`. -**1.4 Decomposition** -1. Count `onesRow` for each row and `onesCol` for each column. -2. Derive `zerosRow[i] = n - onesRow[i]`, `zerosCol[j] = m - onesCol[j]`. -3. For each cell, compute the formula and store in `res`. -4. Return `res`. +Since `zerosRow[i] = n - onesRow[i]` and `zerosCol[j] = m - onesCol[j]`, we can simplify: +- `diff[i][j] = onesRow[i] + onesCol[j] - (n - onesRow[i]) - (m - onesCol[j])` +- `diff[i][j] = 2 * onesRow[i] + 2 * onesCol[j] - n - m` -### Steps - -**2.1 Initialization & Example Setup** -Example: `grid = [[0,1,1],[1,0,1],[0,0,1]]`. - -**2.2 Start Checking** -Compute `onesRow = [2,2,1]`, `onesCol = [1,1,3]`. - -**2.3 Trace Walkthrough** -| Cell (i,j) | onesRow[i] | onesCol[j] | zerosRow[i] | zerosCol[j] | diff | -|------------|------------|------------|-------------|-------------|------| -| (0,0) | 2 | 1 | 1 | 2 | 0 | -| (0,1) | 2 | 1 | 1 | 2 | 0 | -| (0,2) | 2 | 3 | 1 | 0 | 4 | -| ... | ... | ... | ... | ... | ... | - -**2.4 Increment and Loop** -Fill every cell using the precomputed counts. - -**2.5 Return Result** -Produces `[[0,0,4],[0,0,4],[-2,-2,2]]` for the example. -## Explanation - -### Strategy - -**Restate the problem** - -We are given a binary matrix and need to create a difference matrix where each cell `diff[i][j]` is calculated as: ones in row i + ones in column j - zeros in row i - zeros in column j. - -**1.1 Constraints & Complexity** - -- **Input Size:** The matrix can have up to 10^5 rows and columns, with total cells up to 10^5. -- **Time Complexity:** O(m*n) - We need to traverse the matrix twice: once to count ones in rows and columns, and once to build the result matrix. -- **Space Complexity:** O(m + n) - We store arrays for row and column counts. -- **Edge Case:** Empty matrix or single cell matrix are handled naturally by the algorithm. - -**1.2 High-level approach** +However, the direct approach of counting ones first, then building the result is clearer. -The goal is to compute the difference matrix efficiently by precomputing row and column statistics. Instead of recalculating counts for each cell, we count ones in each row and column once, then use these counts to compute each cell's value. +**Brute force vs. optimized strategy** -![Matrix showing row and column counts with difference calculation](https://assets.leetcode.com/uploads/2022/11/06/image-20221106171729-5.png) +* **Brute Force:** For each cell `(i, j)`, count ones in row i and column j separately. This would be O(m * n * (m + n)) time, which is too slow. +* **Optimized:** Count ones in each row and column once (O(m * n)), then build the result matrix using these precomputed values (O(m * n)). This is O(m * n) total time, which is optimal. -**1.3 Brute force vs. optimized strategy** +**Decomposition** -- **Brute Force:** For each cell (i, j), count ones and zeros in row i and column j separately. This results in O(m*n*(m+n)) time complexity, which is inefficient. -- **Optimized Strategy:** Precompute ones count for all rows and columns in O(m*n) time, then compute each cell in O(1) time using the precomputed values. Total time is O(m*n). -- **Why optimized is better:** We avoid redundant calculations by reusing row and column statistics across all cells in the same row or column. - -**1.4 Decomposition** - -1. **Count Row Statistics:** Traverse the matrix once to count the number of ones in each row. -2. **Count Column Statistics:** During the same traversal, count the number of ones in each column. -3. **Calculate Zeros:** For each row, zeros = total columns - ones. For each column, zeros = total rows - ones. -4. **Build Result Matrix:** For each cell (i, j), compute diff[i][j] = ones_row[i] + ones_col[j] - zeros_row[i] - zeros_col[j]. +1. **Count Ones:** First pass through the matrix to count ones in each row and column. +2. **Calculate Zeros:** For each row i, zeros = n - onesRow[i]. For each column j, zeros = m - onesCol[j]. +3. **Build Result:** For each cell `(i, j)`, compute `onesRow[i] + onesCol[j] - zerosRow[i] - zerosCol[j]`. ### Steps -**2.1 Initialization & Example Setup** - -Let's use the example: `grid = [[0,1,1],[1,0,1],[0,0,1]]` - -- Matrix dimensions: 3 rows × 3 columns -- Initialize `ones_row = [0, 0, 0]` and `ones_col = [0, 0, 0]` - -**2.2 Start Counting** - -Traverse the matrix to count ones: -- Row 0: [0,1,1] → ones_row[0] = 2 -- Row 1: [1,0,1] → ones_row[1] = 2 -- Row 2: [0,0,1] → ones_row[2] = 1 - -- Column 0: [0,1,0] → ones_col[0] = 1 -- Column 1: [1,0,0] → ones_col[1] = 1 -- Column 2: [1,1,1] → ones_col[2] = 3 - -**2.3 Trace Walkthrough** - -| Cell (i,j) | ones_row[i] | ones_col[j] | zeros_row[i] | zeros_col[j] | diff[i][j] | -|------------|-------------|-------------|--------------|--------------|------------| -| (0,0) | 2 | 1 | 1 | 2 | 2+1-1-2 = 0 | -| (0,1) | 2 | 1 | 1 | 2 | 2+1-1-2 = 0 | -| (0,2) | 2 | 3 | 1 | 0 | 2+3-1-0 = 4 | -| (1,0) | 2 | 1 | 1 | 2 | 2+1-1-2 = 0 | -| (1,1) | 2 | 1 | 1 | 2 | 2+1-1-2 = 0 | -| (1,2) | 2 | 3 | 1 | 0 | 2+3-1-0 = 4 | -| (2,0) | 1 | 1 | 2 | 2 | 1+1-2-2 = -2 | -| (2,1) | 1 | 1 | 2 | 2 | 1+1-2-2 = -2 | -| (2,2) | 1 | 3 | 2 | 0 | 1+3-2-0 = 2 | - -**2.4 Build Result** - -Create result matrix `res` with dimensions 3×3 and fill each cell using the formula. - -**2.5 Return Result** - -Return `[[0,0,4],[0,0,4],[-2,-2,2]]`, which matches the expected output. +1. **Initialization & Example Setup** + Let's use `grid = [[0,1,1],[1,0,1],[0,0,1]]` as our example. + - Initialize `onesRow = [0, 0, 0]` and `onesCol = [0, 0, 0]`. + - Initialize `res` as a 3x3 matrix filled with zeros. + +2. **Count Ones** + Iterate through the matrix and count ones: + - Row 0: `onesRow[0] = 2` (two ones) + - Row 1: `onesRow[1] = 2` (two ones) + - Row 2: `onesRow[2] = 1` (one one) + - Column 0: `onesCol[0] = 1` (one one) + - Column 1: `onesCol[1] = 1` (one one) + - Column 2: `onesCol[2] = 3` (three ones) + +3. **Build Result Matrix** + For each cell `(i, j)`: + - Calculate `zerosRow[i] = n - onesRow[i]` + - Calculate `zerosCol[j] = m - onesCol[j]` + - Set `res[i][j] = onesRow[i] + onesCol[j] - zerosRow[i] - zerosCol[j]` + +4. **Trace Walkthrough** + +| Cell (i,j) | onesRow[i] | onesCol[j] | zerosRow[i] | zerosCol[j] | Calculation | Result | +|------------|------------|------------|-------------|-------------|-------------|--------| +| (0,0) | 2 | 1 | 1 | 2 | 2+1-1-2 | 0 | +| (0,1) | 2 | 1 | 1 | 2 | 2+1-1-2 | 0 | +| (0,2) | 2 | 3 | 1 | 0 | 2+3-1-0 | 4 | +| (1,0) | 2 | 1 | 1 | 2 | 2+1-1-2 | 0 | +| (1,1) | 2 | 1 | 1 | 2 | 2+1-1-2 | 0 | +| (1,2) | 2 | 3 | 1 | 0 | 2+3-1-0 | 4 | +| (2,0) | 1 | 1 | 2 | 2 | 1+1-2-2 | -2 | +| (2,1) | 1 | 1 | 2 | 2 | 1+1-2-2 | -2 | +| (2,2) | 1 | 3 | 2 | 0 | 1+3-2-0 | 2 | + +5. **Return Result** + Return the completed `res` matrix: `[[0,0,4],[0,0,4],[-2,-2,2]]`. diff --git a/explanations/2785/en.md b/explanations/2785/en.md index cf087d9..03b6ed7 100644 --- a/explanations/2785/en.md +++ b/explanations/2785/en.md @@ -2,52 +2,55 @@ ### Strategy -**Restate the problem** -Sort all vowels in the string by ASCII order while leaving consonants in place. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `1 <= |s| <= 1e5`. -- **Time Complexity:** O(n log n) to sort extracted vowels; O(n) if counting-sort on 10 vowel chars. -- **Space Complexity:** O(n) for the output list. -- **Edge Case:** No vowels → string unchanged. +* **String Length:** The string s has length 1-10^5, consisting of uppercase and lowercase English letters. +* **Time Complexity:** We need to extract vowels (O(n)), sort them (O(k log k) where k is number of vowels), and rebuild the string (O(n)). **Time Complexity: O(n + k log k)** where k is the number of vowels, **Space Complexity: O(n)** for storing vowels and result. +* **Edge Case:** If there are no vowels, return the original string unchanged. -**1.2 High-level approach** -Extract vowels, sort them, then rebuild the string by replacing vowel positions with sorted vowels. -![Extract-sort-rebuild pipeline](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** Repeatedly scan to find next smallest vowel — O(n²). -- **Optimized:** One pass extract + one sort + one rebuild — O(n log n). +The problem asks us to sort vowels in non-decreasing ASCII order while keeping consonants in their original positions. -**1.4 Decomposition** -1. Collect vowels into a list. -2. Sort the vowel list. -3. Rebuild string: if char is vowel, take next from sorted list; else keep consonant. -4. Join result. +**Brute force vs. optimized strategy** -### Steps - -**2.1 Initialization & Example Setup** -Example: `s = "lEetcOde"`; vowels extracted: `['E','e','O','e']`. +* **Brute Force:** For each vowel position, find the next smallest vowel to place there. This would be O(n²) time. +* **Optimized:** Extract all vowels, sort them, then replace vowels in order. This is O(n + k log k) where k is the number of vowels. -**2.2 Start Checking** -Sort vowels → `['E','O','e','e']`. +**Decomposition** -**2.3 Trace Walkthrough** -| Index | Char | Is vowel? | Replacement | Result so far | -|-------|------|-----------|--------------------|---------------| -| 0 | l | No | l | l | -| 1 | E | Yes | E (sorted[0]) | lE | -| 2 | e | Yes | O (sorted[1]) | lEO | -| 3 | t | No | t | lEOt | -| 4 | c | No | c | lEOtc | -| 5 | O | Yes | e (sorted[2]) | lEOtce | -| 6 | d | No | d | lEOtced | -| 7 | e | Yes | e (sorted[3]) | lEOtcede | +1. **Identify Vowels:** Create a set of vowels (both uppercase and lowercase). +2. **Extract and Sort:** Collect all vowels from the string and sort them by ASCII value. +3. **Rebuild String:** Traverse the original string, replacing vowels with sorted vowels in order. -**2.4 Increment and Loop** -Advance through string, consuming sorted vowels sequentially. - -**2.5 Return Result** -Final string: `"lEOtcede"`. +### Steps +1. **Initialization & Example Setup** + Let's use `s = "lEetcOde"` as our example. + - Define vowels set: `{'a','e','i','o','u','A','E','I','O','U'}`. + - Initialize `vowel_list = []` and `res = []`. + +2. **Extract Vowels** + - Traverse s: 'l' (consonant), 'E' (vowel), 'e' (vowel), 't' (consonant), 'c' (consonant), 'O' (vowel), 'd' (consonant), 'e' (vowel). + - `vowel_list = ['E', 'e', 'O', 'e']`. + - Sort: `['E', 'O', 'e', 'e']` (ASCII: E=69, O=79, e=101, e=101). + +3. **Trace Walkthrough** + +| Index | Character | Is Vowel? | Sorted Vowel | Result So Far | +|-------|-----------|-----------|---------------|---------------| +| 0 | 'l' | No | - | "l" | +| 1 | 'E' | Yes | 'E' (idx 0) | "lE" | +| 2 | 'e' | Yes | 'O' (idx 1) | "lEO" | +| 3 | 't' | No | - | "lEOt" | +| 4 | 'c' | No | - | "lEOtc" | +| 5 | 'O' | Yes | 'e' (idx 2) | "lEOtce" | +| 6 | 'd' | No | - | "lEOtced" | +| 7 | 'e' | Yes | 'e' (idx 3) | "lEOtcede" | + +4. **Rebuild String** + - Use index `vowel_idx = 0` to track position in sorted vowel list. + - For each character: if vowel, append `vowel_list[vowel_idx]` and increment; otherwise append original character. + +5. **Return Result** + Return `''.join(res)` = `"lEOtcede"`. diff --git a/explanations/3562/en.md b/explanations/3562/en.md new file mode 100644 index 0000000..b51d12f --- /dev/null +++ b/explanations/3562/en.md @@ -0,0 +1,62 @@ +## Explanation + +### Strategy + +**Constraints & Edge Cases** + +* **Tree Structure:** n employees (1-160), budget is 1-160. The hierarchy forms a tree where employee 1 is the root. If a parent buys stock, children get 50% discount. +* **Time Complexity:** We use tree DP with knapsack for children. For each node, we merge children's results. **Time Complexity: O(n * budget²)** in worst case, **Space Complexity: O(n * budget)** for memoization. +* **Edge Case:** If budget is 0, return 0. If all stocks have negative profit, return 0. + +**High-level approach** + +The problem asks us to maximize profit by buying stocks for employees, with a discount rule: if a parent buys, children get half price. We use tree dynamic programming with knapsack to allocate budget optimally among children. + +**Brute force vs. optimized strategy** + +* **Brute Force:** Try all possible combinations of buying/not buying for each employee. This would be exponential. +* **Optimized:** Use tree DP where for each node, we maintain a dictionary mapping (budget → max_profit). We merge children's results using knapsack approach, considering both buying and skipping the current node. + +**Decomposition** + +1. **Tree Structure:** Build adjacency list from hierarchy. +2. **DFS with State:** For each employee, track whether parent bought (discount available). +3. **Two Options:** Buy current stock or skip it. +4. **Merge Children:** Use knapsack to optimally allocate budget among children. +5. **Return Maximum:** Return the maximum profit from all budget allocations. + +### Steps + +1. **Initialization & Example Setup** + Let's use `n=2, present=[1,2], future=[4,3], hierarchy=[[1,2]], budget=3` as our example. + - Build tree: employee 1 has child employee 2. + - Start DFS from employee 0 (1-indexed → 0-indexed). + +2. **DFS for Employee 0 (no discount)** + - Cost: `present[0] = 1`, Profit: `future[0] - 1 = 3` + - Option 1 (buy): `buy_current = {1: 3}` + - Option 2 (skip): `skip_current = {0: 0}` + - Process child (employee 1): + - If we buy employee 0: child gets discount → `dfs(1, True)` returns `{1: 1}` (cost=1, profit=3-1=2, but wait...) + - Merge: `buy_current` with child discount results + +3. **Trace Walkthrough** + +For employee 0 (root, no parent discount): +- Buy option: cost=1, profit=3, remaining budget=2 + - Child with discount: cost=2//2=1, profit=3-1=2 + - Total: budget=1+1=2, profit=3+2=5 ✓ +- Skip option: budget=3 + - Child no discount: cost=2, profit=3-2=1 + - Total: budget=2, profit=0+1=1 + +Result: `{2: 5, 3: 1}` → max = 5 + +4. **Knapsack Merging** + - For each child, merge results by trying all budget allocations + - `new_buy[spent1 + spent2] = max(new_buy[spent1 + spent2], prof1 + prof2)` + - This ensures optimal budget allocation + +5. **Return Result** + Return `max(result.values()) = 5`. + diff --git a/explanations/797/en.md b/explanations/797/en.md index d003ff6..d980911 100644 --- a/explanations/797/en.md +++ b/explanations/797/en.md @@ -2,115 +2,61 @@ ### Strategy -**Restate the problem** -Given a DAG adjacency list, enumerate all paths from node 0 to node n-1. +**Constraints & Edge Cases** -**1.1 Constraints & Complexity** -- **Input Size:** `2 <= n <= 15`. -- **Time Complexity:** O(P * L), where P is number of paths and L their length (DFS visits each edge per path). -- **Space Complexity:** O(L) recursion stack + output. -- **Edge Case:** Single edge 0→1. +* **Graph Size:** The DAG has 2-15 nodes labeled 0 to n-1. The graph is guaranteed to be acyclic and connected from node 0 to node n-1. +* **Time Complexity:** We use DFS to explore all paths. In worst case, there can be exponential number of paths, but with n ≤ 15, this is manageable. **Time Complexity: O(2^n)** in worst case, **Space Complexity: O(n)** for recursion stack and path storage. +* **Edge Case:** If there's only one path from 0 to n-1, we return that single path. -**1.2 High-level approach** -DFS from 0, accumulate nodes in a path; when reaching n-1, record the path. -![DFS path enumeration](https://assets.leetcode.com/static_assets/public/images/LeetCode_logo.png) +**High-level approach** -**1.3 Brute force vs. optimized strategy** -- **Brute Force:** None better is needed; DFS naturally enumerates all paths. -- **Optimized:** Use backtracking to avoid extra copies while exploring. +The problem asks us to find all paths from node 0 to node n-1 in a directed acyclic graph. We use depth-first search to explore all possible paths. -**1.4 Decomposition** -1. Start DFS at node 0 with path `[0]`. -2. For each neighbor, append, recurse, then pop. -3. If node == n-1, push a copy of path to results. -4. Return all collected paths. +**Brute force vs. optimized strategy** -### Steps - -**2.1 Initialization & Example Setup** -Example: `graph = [[1,2],[3],[3],[]]`, n=4. - -**2.2 Start Checking** -Begin at 0; explore neighbors 1 and 2. - -**2.3 Trace Walkthrough** -| Path stack | Next step | Action | -|------------|------------------|------------------| -| [0] | neighbor 1 | recurse | -| [0,1] | neighbor 3 | reach n-1 → save | -| [0] | neighbor 2 | recurse | -| [0,2] | neighbor 3 | reach n-1 → save | - -**2.4 Increment and Loop** -Backtrack after each neighbor; continue until all explored. - -**2.5 Return Result** -`[[0,1,3],[0,2,3]]` for the example. -## Explanation - -### Strategy - -**Restate the problem** - -We need to find all paths from node 0 to node n-1 in a directed acyclic graph (DAG). Each path is a sequence of nodes connected by edges. - -**1.1 Constraints & Complexity** - -- **Input Size:** The graph has 2 to 15 nodes. -- **Time Complexity:** O(2^n * n) - In the worst case, there can be exponentially many paths, and each path can have up to n nodes. -- **Space Complexity:** O(2^n * n) - We need to store all paths, and the recursion stack can go up to n levels. -- **Edge Case:** If there are no paths from 0 to n-1, we return an empty list. - -**1.2 High-level approach** +* **Brute Force:** This is essentially what we do - explore all paths. Since the graph is acyclic, we don't need to worry about cycles, making DFS straightforward. +* **Optimized:** Use DFS with backtracking. When we reach node n-1, we save the current path. We backtrack to explore other paths. -Use depth-first search (DFS) to explore all possible paths from the source node (0) to the target node (n-1). As we traverse, we maintain the current path and add it to results when we reach the destination. +**Decomposition** -![Graph showing all paths from source to target](https://assets.leetcode.com/uploads/2020/09/28/all_1.jpg) - -**1.3 Brute force vs. optimized strategy** - -- **Brute Force:** Generate all possible paths and filter those that end at n-1. This is inefficient and doesn't leverage the DAG structure. -- **Optimized Strategy:** Use DFS with backtracking to explore paths incrementally, only following edges that can potentially lead to the target. This naturally finds all valid paths. -- **Why optimized is better:** DFS with backtracking efficiently explores the graph structure without redundant computations, and backtracking allows us to reuse the path list. - -**1.4 Decomposition** - -1. **Initialize Result:** Create an empty list to store all paths. -2. **Start DFS:** Begin DFS from node 0 with an initial path containing just node 0. -3. **Explore Neighbors:** For each neighbor of the current node, add it to the path and recursively explore. -4. **Record Complete Paths:** When we reach node n-1, add the current path to results. -5. **Backtrack:** After exploring a neighbor, remove it from the path to try other paths. +1. **DFS Traversal:** Start from node 0, recursively visit all neighbors. +2. **Path Tracking:** Maintain current path as we traverse. +3. **Result Collection:** When we reach node n-1, add the current path to results. +4. **Backtracking:** Remove current node from path before returning to explore other paths. ### Steps -**2.1 Initialization & Example Setup** - -Let's use the example: `graph = [[1,2],[3],[3],[]]` - -- Nodes: 0, 1, 2, 3 -- Edges: 0→1, 0→2, 1→3, 2→3 -- Target: node 3 - -**2.2 Start DFS** - -Initialize `res = []` and start DFS from node 0 with `path = [0]`. - -**2.3 Trace Walkthrough** - -| Current Node | Path | Neighbors | Action | Result | -|-------------|------|-----------|--------|--------| -| 0 | [0] | [1,2] | Explore 1 | - | -| 1 | [0,1] | [3] | Explore 3 | - | -| 3 | [0,1,3] | [] | Reached target! | Add [0,1,3] | -| 1 | [0,1] | [3] | Backtrack | - | -| 0 | [0] | [1,2] | Explore 2 | - | -| 2 | [0,2] | [3] | Explore 3 | - | -| 3 | [0,2,3] | [] | Reached target! | Add [0,2,3] | - -**2.4 Backtrack and Continue** - -After finding path [0,1,3], we backtrack by removing 3 and 1 from the path, returning to node 0. Then we explore the other neighbor (2) to find [0,2,3]. - -**2.5 Return Result** - -After DFS completes, `res = [[0,1,3],[0,2,3]]`, which contains all paths from node 0 to node 3. +1. **Initialization & Example Setup** + Let's use `graph = [[1,2],[3],[3],[]]` as our example. + - Initialize `res = []` to store all paths. + - Start DFS from node 0 with path `[0]`. + +2. **DFS Function** + The `dfs(node, path)` function: + - If `node == n - 1`, we found a path → append `path[:]` to `res`. + - For each neighbor in `graph[node]`: + - Add neighbor to path. + - Recursively call `dfs(neighbor, path)`. + - Remove neighbor from path (backtrack). + +3. **Trace Walkthrough** + +Starting from node 0: + +| Step | Current Node | Path | Neighbors | Action | +|------|--------------|------|-----------|--------| +| 1 | 0 | [0] | [1,2] | Go to 1 | +| 2 | 1 | [0,1]| [3] | Go to 3 | +| 3 | 3 | [0,1,3] | [] | Found path! Add [0,1,3] | +| 4 | 1 | [0,1]| [3] | Backtrack, return | +| 5 | 0 | [0] | [1,2] | Go to 2 | +| 6 | 2 | [0,2]| [3] | Go to 3 | +| 7 | 3 | [0,2,3] | [] | Found path! Add [0,2,3] | + +4. **Backtracking** + After exploring a path, we remove the last node to try other branches: + - After finding [0,1,3], remove 3, then remove 1. + - Now we can explore path through node 2. + +5. **Return Result** + Return `res = [[0,1,3], [0,2,3]]`. diff --git a/solutions/1008/01.py b/solutions/1008/01.py index 135eddd..77eedd9 100644 --- a/solutions/1008/01.py +++ b/solutions/1008/01.py @@ -4,28 +4,19 @@ # self.val = val # self.left = left # self.right = right -from typing import List, Optional - class Solution: def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]: if not preorder: return None root = TreeNode(preorder[0]) - stack = [root] - for val in preorder[1:]: - node = TreeNode(val) - - # If current value is less than stack top, it's a left child - if val < stack[-1].val: - stack[-1].left = node - else: - # Find the parent where this node should be right child - while stack and stack[-1].val < val: - parent = stack.pop() - parent.right = node - - stack.append(node) + # Find the split point where values become greater than root + i = 1 + while i < len(preorder) and preorder[i] < preorder[0]: + i += 1 + + root.left = self.bstFromPreorder(preorder[1:i]) + root.right = self.bstFromPreorder(preorder[i:]) return root diff --git a/solutions/1079/01.py b/solutions/1079/01.py index d529fa9..c51ed0d 100644 --- a/solutions/1079/01.py +++ b/solutions/1079/01.py @@ -1,20 +1,16 @@ -from collections import Counter - class Solution: def numTilePossibilities(self, tiles: str) -> int: - count = Counter(tiles) - res = 0 + from collections import Counter - def backtrack(): - nonlocal res + def backtrack(count): + res = 0 for char in count: if count[char] > 0: - # Use this character + res += 1 # Count this sequence count[char] -= 1 - res += 1 - backtrack() - # Backtrack + res += backtrack(count) count[char] += 1 + return res - backtrack() - return res + count = Counter(tiles) + return backtrack(count) diff --git a/solutions/1261/01.py b/solutions/1261/01.py index 307b0df..605fe9e 100644 --- a/solutions/1261/01.py +++ b/solutions/1261/01.py @@ -4,18 +4,18 @@ # self.val = val # self.left = left # self.right = right -from typing import Optional - class FindElements: + def __init__(self, root: Optional[TreeNode]): self.values = set() def recover(node, val): - if node: - node.val = val - self.values.add(val) - recover(node.left, 2 * val + 1) - recover(node.right, 2 * val + 2) + if not node: + return + node.val = val + self.values.add(val) + recover(node.left, 2 * val + 1) + recover(node.right, 2 * val + 2) recover(root, 0) diff --git a/solutions/1347/01.py b/solutions/1347/01.py index 06fd461..867ec5e 100644 --- a/solutions/1347/01.py +++ b/solutions/1347/01.py @@ -1,14 +1,13 @@ -from collections import Counter - class Solution: def minSteps(self, s: str, t: str) -> int: + from collections import Counter + count_s = Counter(s) count_t = Counter(t) res = 0 - # Count characters that need to be changed in t for char in count_s: - if count_t[char] < count_s[char]: + if count_s[char] > count_t[char]: res += count_s[char] - count_t[char] return res diff --git a/solutions/1551/01.py b/solutions/1551/01.py index c3f0a2a..daa7612 100644 --- a/solutions/1551/01.py +++ b/solutions/1551/01.py @@ -1,15 +1,15 @@ class Solution: def minOperations(self, n: int) -> int: - # The array is [1, 3, 5, ..., 2*n-1] - # Target value is the average: (1 + 2*n-1) / 2 = n - # For each element at index i, value is 2*i + 1 - # Operations needed to make it equal to n: |(2*i + 1) - n| - # But we only need to count half since we can pair operations + # Target value is the average + target = n + # Calculate operations needed res = 0 - for i in range(n // 2): - target = n + for i in range(n): current = 2 * i + 1 - res += target - current + if current < target: + res += target - current + else: + break return res diff --git a/solutions/1605/01.py b/solutions/1605/01.py index 95e200c..f7ad8d3 100644 --- a/solutions/1605/01.py +++ b/solutions/1605/01.py @@ -1,5 +1,3 @@ -from typing import List - class Solution: def restoreMatrix(self, rowSum: List[int], colSum: List[int]) -> List[List[int]]: m, n = len(rowSum), len(colSum) @@ -7,7 +5,6 @@ def restoreMatrix(self, rowSum: List[int], colSum: List[int]) -> List[List[int]] for i in range(m): for j in range(n): - # Place the minimum of remaining rowSum and colSum val = min(rowSum[i], colSum[j]) res[i][j] = val rowSum[i] -= val diff --git a/solutions/1630/01.py b/solutions/1630/01.py index 21e3546..cc6b8b3 100644 --- a/solutions/1630/01.py +++ b/solutions/1630/01.py @@ -1,26 +1,23 @@ -from typing import List - class Solution: def checkArithmeticSubarrays(self, nums: List[int], l: List[int], r: List[int]) -> List[bool]: res = [] for i in range(len(l)): - # Extract subarray subarray = nums[l[i]:r[i] + 1] + subarray.sort() - # Sort the subarray - sorted_sub = sorted(subarray) - - # Check if it's arithmetic - if len(sorted_sub) <= 1: + if len(subarray) < 2: res.append(True) - else: - diff = sorted_sub[1] - sorted_sub[0] - is_arithmetic = True - for j in range(2, len(sorted_sub)): - if sorted_sub[j] - sorted_sub[j - 1] != diff: - is_arithmetic = False - break - res.append(is_arithmetic) + continue + + diff = subarray[1] - subarray[0] + is_arithmetic = True + + for j in range(2, len(subarray)): + if subarray[j] - subarray[j - 1] != diff: + is_arithmetic = False + break + + res.append(is_arithmetic) return res diff --git a/solutions/1823/01.py b/solutions/1823/01.py index 42eb7e9..5c7e807 100644 --- a/solutions/1823/01.py +++ b/solutions/1823/01.py @@ -1,9 +1,7 @@ class Solution: def findTheWinner(self, n: int, k: int) -> int: - # Use Josephus problem solution - # For 0-indexed: winner = (winner + k) % n - # Convert to 1-indexed at the end - winner = 0 - for i in range(2, n + 1): - winner = (winner + k) % i - return winner + 1 + # Josephus problem - 1-indexed + res = 0 + for i in range(1, n + 1): + res = (res + k) % i + return res + 1 diff --git a/solutions/2120/01.py b/solutions/2120/01.py index 1204b10..0ee4116 100644 --- a/solutions/2120/01.py +++ b/solutions/2120/01.py @@ -1,32 +1,26 @@ -from typing import List - class Solution: def executeInstructions(self, n: int, startPos: List[int], s: str) -> List[int]: - m = len(s) res = [] - - # Direction mapping - directions = { - 'L': (0, -1), - 'R': (0, 1), - 'U': (-1, 0), - 'D': (1, 0) - } + m = len(s) for i in range(m): row, col = startPos[0], startPos[1] count = 0 for j in range(i, m): - dr, dc = directions[s[j]] - new_row, new_col = row + dr, col + dc + if s[j] == 'L': + col -= 1 + elif s[j] == 'R': + col += 1 + elif s[j] == 'U': + row -= 1 + else: # 'D' + row += 1 - # Check if next move is valid - if 0 <= new_row < n and 0 <= new_col < n: - row, col = new_row, new_col - count += 1 - else: + if row < 0 or row >= n or col < 0 or col >= n: break + + count += 1 res.append(count) diff --git a/solutions/2221/01.py b/solutions/2221/01.py index 80b7ec2..0ce7a92 100644 --- a/solutions/2221/01.py +++ b/solutions/2221/01.py @@ -1,5 +1,3 @@ -from typing import List - class Solution: def triangularSum(self, nums: List[int]) -> int: while len(nums) > 1: diff --git a/solutions/2326/01.py b/solutions/2326/01.py index eb9c40a..04e1bcc 100644 --- a/solutions/2326/01.py +++ b/solutions/2326/01.py @@ -3,24 +3,19 @@ # def __init__(self, val=0, next=None): # self.val = val # self.next = next -from typing import List, Optional - class Solution: def spiralMatrix(self, m: int, n: int, head: Optional[ListNode]) -> List[List[int]]: - # Initialize matrix with -1 res = [[-1] * n for _ in range(m)] - # Directions: right, down, left, up directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] dir_idx = 0 row, col = 0, 0 - current = head - while current: - res[row][col] = current.val - current = current.next + while head: + res[row][col] = head.val + head = head.next - if not current: + if not head: break # Try to move in current direction diff --git a/solutions/237/01.py b/solutions/237/01.py index 3a2a0c2..f9bbb08 100644 --- a/solutions/237/01.py +++ b/solutions/237/01.py @@ -10,7 +10,5 @@ def deleteNode(self, node): :type node: ListNode :rtype: void Do not return anything, modify node in-place instead. """ - # Copy value from next node node.val = node.next.val - # Skip next node node.next = node.next.next diff --git a/solutions/2482/01.py b/solutions/2482/01.py index f1fc9cc..451639a 100644 --- a/solutions/2482/01.py +++ b/solutions/2482/01.py @@ -1,5 +1,3 @@ -from typing import List - class Solution: def onesMinusZeros(self, grid: List[List[int]]) -> List[List[int]]: m, n = len(grid), len(grid[0]) @@ -15,14 +13,11 @@ def onesMinusZeros(self, grid: List[List[int]]) -> List[List[int]]: onesCol[j] += 1 # Build result matrix - res = [] + res = [[0] * n for _ in range(m)] for i in range(m): - row = [] + zerosRow = n - onesRow[i] for j in range(n): - zerosRow = n - onesRow[i] zerosCol = m - onesCol[j] - diff = onesRow[i] + onesCol[j] - zerosRow - zerosCol - row.append(diff) - res.append(row) + res[i][j] = onesRow[i] + onesCol[j] - zerosRow - zerosCol return res diff --git a/solutions/2785/01.py b/solutions/2785/01.py index c0aac64..3e7bd60 100644 --- a/solutions/2785/01.py +++ b/solutions/2785/01.py @@ -2,7 +2,7 @@ class Solution: def sortVowels(self, s: str) -> str: vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'} - # Extract vowels and sort them + # Extract and sort vowels vowel_list = [c for c in s if c in vowels] vowel_list.sort() diff --git a/solutions/3562/01.py b/solutions/3562/01.py new file mode 100644 index 0000000..5c4f279 --- /dev/null +++ b/solutions/3562/01.py @@ -0,0 +1,66 @@ +class Solution: + def maxProfit(self, n: int, present: List[int], future: List[int], hierarchy: List[List[int]], budget: int) -> int: + from collections import defaultdict + from functools import lru_cache + + # Build adjacency list (0-indexed) + adj_list = defaultdict(list) + for u, v in hierarchy: + adj_list[u - 1].append(v - 1) + + @lru_cache(maxsize=None) + def dfs(employee, has_discount): + # Calculate cost and profit for current employee + cost = present[employee] // 2 if has_discount else present[employee] + profit = future[employee] - cost + + # Option 1: Buy current stock + buy_current = {} + if cost <= budget: + buy_current[cost] = profit + + # Option 2: Skip current stock + skip_current = {0: 0} + + # Process children using knapsack approach + for child in adj_list[employee]: + # Get child results with and without discount + child_with_discount = dfs(child, True) # If we buy current, child gets discount + child_no_discount = dfs(child, False) # If we skip current, child has no discount + + # Merge buy_current with child results + new_buy = {} + for spent, prof in buy_current.items(): + for child_spent, child_prof in child_with_discount.items(): + total_spent = spent + child_spent + if total_spent <= budget: + total_prof = prof + child_prof + if total_spent not in new_buy or new_buy[total_spent] < total_prof: + new_buy[total_spent] = total_prof + buy_current = new_buy + + # Merge skip_current with child results + new_skip = {} + for spent, prof in skip_current.items(): + for child_spent, child_prof in child_no_discount.items(): + total_spent = spent + child_spent + if total_spent <= budget: + total_prof = prof + child_prof + if total_spent not in new_skip or new_skip[total_spent] < total_prof: + new_skip[total_spent] = total_prof + skip_current = new_skip + + # Merge buy and skip results + result = {} + for spent, prof in buy_current.items(): + if spent not in result or result[spent] < prof: + result[spent] = prof + for spent, prof in skip_current.items(): + if spent not in result or result[spent] < prof: + result[spent] = prof + + return result + + result = dfs(0, False) # Root has no parent, so no discount + return max(result.values()) if result else 0 + diff --git a/solutions/797/01.py b/solutions/797/01.py index 28a1a81..9f041f3 100644 --- a/solutions/797/01.py +++ b/solutions/797/01.py @@ -1,5 +1,3 @@ -from typing import List - class Solution: def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: res = []