Skip to content

Commit cef971c

Browse files
Merge pull request #126 from romankurnovskii/problems-944-3665-3669-3750
Add solutions and explanations for problems 944, 3665, 3669, and update explanation for 3750
2 parents 78d7da6 + e7c4bbd commit cef971c

File tree

7 files changed

+359
-31
lines changed

7 files changed

+359
-31
lines changed

explanations/3665/en.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We need to count the number of unique paths from the top-left corner (0,0) to the bottom-right corner (m-1, n-1) in a grid where mirrors can reflect the robot's movement. The robot can only move right or down. When attempting to move into a mirror cell, the robot is reflected: moving right into a mirror reflects down, and moving down into a mirror reflects right. If a reflection lands in another mirror, the process continues.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** Grid dimensions m and n can be up to 500.
10+
- **Time Complexity:** O(m * n) - we use memoization with 3D DP (i, j, direction), and each state is computed at most once.
11+
- **Space Complexity:** O(m * n) - we store a 3D DP table of size m * n * 2 (two directions per cell).
12+
- **Edge Case:** If the starting or ending cell is a mirror, the problem states they are always 0 (non-mirror), so we don't need to handle that case.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to use dynamic programming with memoization, tracking not just the position (i, j) but also the direction from which we arrived at that position. This is crucial because when we're at a mirror cell, the reflection depends on the direction we came from.
17+
18+
![Mirror path reflection visualization](https://assets.leetcode.com/static_assets/others/mirror-reflection.png)
19+
20+
**1.3 Brute force vs. optimized strategy:**
21+
22+
- **Brute Force:** Try all possible paths recursively without memoization, which would be exponential O(2^(m+n)) in the worst case.
23+
- **Optimized Strategy:** Use 3D dynamic programming with memoization, tracking position (i, j) and direction (dir). The direction is necessary because when at a mirror, the reflection depends on how we arrived. This is O(m * n) time.
24+
- **Optimization:** By memoizing states (i, j, dir), we avoid recalculating the same subproblems, reducing time complexity from exponential to polynomial.
25+
26+
**1.4 Decomposition:**
27+
28+
1. Use a 3D DP table where dp[i][j][dir] represents the number of ways to reach (i,j) with direction dir.
29+
2. Direction dir = 0 means we came from the left (moved right to reach here), dir = 1 means we came from above (moved down to reach here).
30+
3. For each cell (i, j) with direction dir:
31+
- If the cell is a mirror (grid[i][j] == 1), reflect based on direction:
32+
- If dir == 0 (came from left), reflect down (move to i+1, j with dir=1).
33+
- If dir == 1 (came from above), reflect right (move to i, j+1 with dir=0).
34+
- If the cell is not a mirror, try both moves: right and down.
35+
4. Use memoization to avoid recalculating the same states.
36+
5. Return the result modulo 10^9 + 7.
37+
38+
### Steps (The "How")
39+
40+
**2.1 Initialization & Example Setup:**
41+
42+
Let's use the example: `grid = [[0,1,0],[0,0,1],[1,0,0]]`
43+
44+
- Grid dimensions: m = 3, n = 3
45+
- Starting position: (0, 0) with dir = 0
46+
- Destination: (2, 2)
47+
- Mirrors at: (0,1), (1,2), (2,0)
48+
49+
**2.2 Start Processing:**
50+
51+
We begin recursive DFS from (0, 0) with direction 0.
52+
53+
**2.3 Trace Walkthrough:**
54+
55+
| Position (i,j) | Direction | Cell Type | Action | Next States |
56+
|----------------|-----------|-----------|--------|-------------|
57+
| (0,0) | 0 | 0 (empty) | Try both moves | (0,1) dir=0, (1,0) dir=1 |
58+
| (0,1) | 0 | 1 (mirror) | Reflect down | (1,1) dir=1 |
59+
| (1,0) | 1 | 0 (empty) | Try both moves | (1,1) dir=0, (2,0) dir=1 |
60+
| (1,1) | 0 | 0 (empty) | Try both moves | (1,2) dir=0, (2,1) dir=1 |
61+
| (1,1) | 1 | 0 (empty) | Try both moves | (1,2) dir=0, (2,1) dir=1 |
62+
| (1,2) | 0 | 1 (mirror) | Reflect down | (2,2) dir=1 ✓ |
63+
| (2,0) | 1 | 1 (mirror) | Reflect right | (2,1) dir=0 |
64+
| (2,1) | 0 | 0 (empty) | Try both moves | (2,2) dir=0 ✓ |
65+
66+
**2.4 Increment and Loop:**
67+
68+
The recursive DFS explores all valid paths, using memoization to avoid recalculating the same (i, j, dir) states.
69+
70+
**2.5 Return Result:**
71+
72+
The result is 5, which represents the total number of unique valid paths from (0,0) to (2,2) considering all mirror reflections.
73+

explanations/3669/en.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We need to split an integer n into exactly k positive integers such that their product equals n, and we want to minimize the maximum difference between any two numbers in the split.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** n can be up to 10^5, and k is between 2 and 5.
10+
- **Time Complexity:** O(d^k) where d is the number of divisors of n - we use backtracking to try all combinations of k divisors. Since k is small (≤5) and the number of divisors is typically small, this is feasible.
11+
- **Space Complexity:** O(d + k) - we store all divisors and the current path during backtracking.
12+
- **Edge Case:** If n is a prime number, the only valid split is [1, 1, ..., 1, n] (k-1 ones and n).
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to find all divisors of n, then use backtracking to find a combination of k divisors whose product equals n, while minimizing the difference between the maximum and minimum values in the combination.
17+
18+
![Factor decomposition visualization](https://assets.leetcode.com/static_assets/others/factor-decomposition.png)
19+
20+
**1.3 Brute force vs. optimized strategy:**
21+
22+
- **Brute Force:** Try all possible combinations of k numbers that multiply to n, which would be exponential and inefficient.
23+
- **Optimized Strategy:** First find all divisors of n, then use backtracking to explore combinations of k divisors. We can prune early when the product exceeds n. This is O(d^k) where d is the number of divisors.
24+
- **Optimization:** By working only with divisors and pruning branches where the product exceeds n, we significantly reduce the search space compared to trying all possible number combinations.
25+
26+
**1.4 Decomposition:**
27+
28+
1. Find all divisors of n by checking numbers from 1 to sqrt(n).
29+
2. Sort the divisors for efficient backtracking.
30+
3. Use backtracking (DFS) to try combinations of k divisors:
31+
- Start with an empty path and product = 1.
32+
- For each divisor, try adding it to the path and recursively explore.
33+
- If we have k divisors and product equals n, check if this combination has a smaller max-min difference.
34+
- Prune branches where product exceeds n.
35+
4. Return the combination with the minimum max-min difference.
36+
37+
### Steps (The "How")
38+
39+
**2.1 Initialization & Example Setup:**
40+
41+
Let's use the example: `n = 100`, `k = 2`
42+
43+
- Divisors of 100: [1, 2, 4, 5, 10, 20, 25, 50, 100]
44+
- We need to find 2 divisors whose product is 100 and minimize max - min.
45+
46+
**2.2 Start Processing:**
47+
48+
We begin backtracking with an empty path, product = 1, and picked = 0.
49+
50+
**2.3 Trace Walkthrough:**
51+
52+
| Step | Path | Product | Picked | Action | Valid? |
53+
|------|------|---------|---------|--------|--------|
54+
| 1 | [1] | 1 | 1 | Add divisor 1 | Continue |
55+
| 2 | [1, 1] | 1 | 2 | Add divisor 1 | Product < 100, continue |
56+
| 3 | [1, 100] | 100 | 2 | Add divisor 100 | Product = 100, diff = 99 |
57+
| 4 | [1, 50] | 50 | 2 | Try divisor 50 | Product < 100, backtrack |
58+
| 5 | [10, 10] | 100 | 2 | Try divisor 10 | Product = 100, diff = 0 ✓ |
59+
60+
The best combination is [10, 10] with difference 0.
61+
62+
**2.4 Increment and Loop:**
63+
64+
The backtracking algorithm explores all valid combinations, keeping track of the best result (minimum difference).
65+
66+
**2.5 Return Result:**
67+
68+
The result is [10, 10], which has a product of 100 and a maximum difference of 0, which is the minimum possible.
69+

explanations/3750/en.md

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,62 @@
11
## Explanation
22

3-
### Strategy
3+
### Strategy (The "Why")
44

5-
**Constraints & Edge Cases**
5+
**Restate the problem:** We need to find the minimum number of bit flips required to make an integer equal to its binary reverse. The binary reverse of a number is obtained by reversing its binary representation.
66

7-
* **Binary Representation:** We are working with the bits of an integer $n$. The length of the binary string depends on the magnitude of $n$ (up to $\approx 30$ bits for standard integers).
8-
* **Time Complexity:** Since the number of bits is small ($\log_2 n$), the solution will be very fast, effectively $O(\log n)$.
9-
* **Edge Case:** If $n=0$ or $n$ is a single bit (e.g., 1), the reverse is identical to the original, so the answer is 0.
7+
**1.1 Constraints & Complexity:**
108

11-
**High-level approach**
12-
The problem asks for the minimum flips to make a number equal to its **original** binary reverse.
13-
Imagine the binary string as a row of lights. We compare the light at the very start (left) with the light at the very end (right).
9+
- **Input Size:** The integer n can be up to 2^31 - 1, which means up to approximately 31 bits in binary representation.
10+
- **Time Complexity:** O(log n) - we need to process each bit in the binary representation once, and the number of bits is logarithmic in n.
11+
- **Space Complexity:** O(log n) - we store the binary string representation, which has length proportional to log n.
12+
- **Edge Case:** If n = 0 or n has only one bit (e.g., 1), the reverse is identical to the original, so the answer is 0.
1413

15-
* If the left bit is `1` and the right bit is `0`, we have a mismatch.
16-
* To make the number equal to its reverse, the left position *must* become what the right position was, and the right position *must* become what the left position was. This requires flipping **both** bits.
17-
* Therefore, every mismatching symmetric pair costs exactly **2 flips**.
14+
**1.2 High-level approach:**
1815

19-
**Brute force vs. optimized strategy**
16+
The goal is to compare symmetric bit positions in the binary representation and count how many pairs need to be flipped to make the number equal to its reverse.
2017

21-
* **Brute Force:** Calculate the reverse of $n$ separately, then iterate through every bit of $n$ and the reverse to count differences.
22-
* **Optimized (Two Pointers):** We extract the binary string once. We place pointers at the start and end. We move them inward, counting mismatches. This is efficient and requires only one pass over the bits.
18+
![Binary bit comparison visualization](https://assets.leetcode.com/static_assets/others/binary-comparison.png)
2319

24-
**Decomposition**
20+
**1.3 Brute force vs. optimized strategy:**
2521

26-
1. **Bit Extraction:** Convert the integer $n$ into its binary string format (removing the '0b' prefix).
27-
2. **Two-Pointer Scan:** Initialize `left` at index 0 and `right` at the last index.
28-
3. **Check Pairs:** If `s[left] != s[right]`, we found a mismatch. Add 2 to our result (1 flip for each side).
29-
4. **Converge:** Move `left` forward and `right` backward until they meet.
22+
- **Brute Force:** Calculate the reverse of n separately, then iterate through every bit of n and the reverse to count differences. This requires creating the full reverse and comparing bit by bit.
23+
- **Optimized Strategy:** Use two pointers starting from both ends of the binary string, moving inward while comparing symmetric bits. This is O(log n) time and only requires one pass.
24+
- **Optimization:** By using two pointers, we avoid creating a separate reversed binary string and can process the comparison in a single pass, making the solution more efficient.
3025

31-
### Steps
26+
**1.4 Decomposition:**
3227

33-
1. **Convert to Binary**
34-
Turn the integer $n$ into a string of bits. For example, if $n=10$, we get the string `"1010"`.
28+
1. Convert the integer to its binary string representation (removing the '0b' prefix).
29+
2. Initialize two pointers at the start and end of the binary string.
30+
3. Compare bits at symmetric positions (left and right pointers).
31+
4. If bits differ, we need to flip both bits (add 2 to the result).
32+
5. Move pointers inward until they meet.
33+
6. Return the total number of flips needed.
3534

36-
2. **Initialize Pointers**
37-
Set a variable `res` to 0. Set `l` to the start of the string and `r` to the end.
35+
### Steps (The "How")
3836

39-
3. **Iterate and Compare**
40-
While `l` is less than `r`:
37+
**2.1 Initialization & Example Setup:**
4138

42-
* Check if the bit at `l` is different from the bit at `r`.
43-
* **Why?** If the bits are different (e.g., `1` and `0`), then to swap their values effectively (so the number equals its reverse), both positions must be flipped.
44-
* If they differ, add **2** to `res`.
39+
Let's use the example: `n = 10`
4540

46-
4. **Close the Window**
47-
Increment `l` and decrement `r` to check the next pair of bits. Continue until the pointers meet in the middle.
41+
- Binary representation: `bin(10) = "0b1010"`, so `s = "1010"`
42+
- Left pointer `l = 0`, right pointer `r = 3`
43+
- Result `res = 0`
44+
45+
**2.2 Start Checking:**
46+
47+
We begin comparing bits from both ends of the binary string.
48+
49+
**2.3 Trace Walkthrough:**
50+
51+
| Step | l | r | s[l] | s[r] | Match? | Action | res |
52+
| ---- | --- | --- | ---- | ---- | ------ | --------------------- | --- |
53+
| 1 | 0 | 3 | '1' | '0' | No | Flip both bits, add 2 | 2 |
54+
| 2 | 1 | 2 | '0' | '1' | No | Flip both bits, add 2 | 4 |
55+
56+
**2.4 Increment and Loop:**
57+
58+
After each comparison, we increment `l` and decrement `r` to move toward the center. We continue until `l >= r`.
59+
60+
**2.5 Return Result:**
61+
62+
The result is 4, which means we need to flip 4 bits (2 pairs) to make the number equal to its binary reverse.

explanations/944/en.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We are given an array of strings, all of the same length. When arranged in a grid (one string per row), we need to count how many columns are not sorted lexicographically (in non-decreasing order).
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** The number of strings n can be up to 100, and each string length m can be up to 1000.
10+
- **Time Complexity:** O(n * m) - we need to check each column, and for each column, we check all rows to see if it's sorted.
11+
- **Space Complexity:** O(1) - we only use a constant amount of extra space to track the result.
12+
- **Edge Case:** If all columns are sorted, we return 0. If there's only one string, all columns are trivially sorted.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to check each column of the grid to see if it's sorted lexicographically (each character is greater than or equal to the character above it). We count how many columns fail this check.
17+
18+
![Column sorting visualization](https://assets.leetcode.com/static_assets/others/column-sorting.png)
19+
20+
**1.3 Brute force vs. optimized strategy:**
21+
22+
- **Brute Force:** This is already the optimal approach - we must check each column and each row in that column to determine if it's sorted.
23+
- **Optimized Strategy:** Iterate through each column, and for each column, check if any row violates the sorted order (current character < previous character). If we find a violation, we can immediately mark that column as unsorted and move to the next column. This is O(n * m) time.
24+
- **Optimization:** By breaking early when we find a violation in a column, we avoid unnecessary comparisons, though the worst-case complexity remains the same.
25+
26+
**1.4 Decomposition:**
27+
28+
1. Get the dimensions: number of strings (rows) n and string length (columns) m.
29+
2. For each column from 0 to m-1:
30+
- Check if the column is sorted by comparing each row with the previous row.
31+
- If we find any row where the character is less than the character in the previous row, the column is not sorted.
32+
- Increment the result counter for each unsorted column.
33+
3. Return the total count of unsorted columns.
34+
35+
### Steps (The "How")
36+
37+
**2.1 Initialization & Example Setup:**
38+
39+
Let's use the example: `strs = ["cba", "daf", "ghi"]`
40+
41+
- Number of rows n = 3, number of columns m = 3
42+
- Grid arrangement:
43+
```
44+
c b a
45+
d a f
46+
g h i
47+
```
48+
- Result `res = 0`
49+
50+
**2.2 Start Checking:**
51+
52+
We iterate through each column and check if it's sorted.
53+
54+
**2.3 Trace Walkthrough:**
55+
56+
| Column | Row 0 | Row 1 | Row 2 | Is Sorted? | Action | res |
57+
|--------|-------|-------|-------|------------|--------|-----|
58+
| 0 | 'c' | 'd' | 'g' | Yes (c < d < g) | Continue | 0 |
59+
| 1 | 'b' | 'a' | 'h' | No (b > a) | Increment res | 1 |
60+
| 2 | 'a' | 'f' | 'i' | Yes (a < f < i) | Continue | 1 |
61+
62+
**2.4 Increment and Loop:**
63+
64+
After checking each column, we move to the next column. If we find a violation (current character < previous character), we immediately break from the inner loop and move to the next column.
65+
66+
**2.5 Return Result:**
67+
68+
The result is 1, which means we need to delete 1 column (column 1) because it's not sorted lexicographically.
69+

solutions/3665/01.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class Solution:
2+
def uniquePaths(self, grid: List[List[int]]) -> int:
3+
MOD = 10**9 + 7
4+
m, n = len(grid), len(grid[0])
5+
6+
# 3D DP: dp[i][j][dir] = number of ways to reach (i,j) with direction dir
7+
# dir = 0: came from left (moved right to reach here)
8+
# dir = 1: came from above (moved down to reach here)
9+
dp = [[[-1, -1] for _ in range(n)] for _ in range(m)]
10+
11+
def dfs(i, j, dir):
12+
# Base case: reached destination
13+
if i == m - 1 and j == n - 1:
14+
return 1
15+
16+
# Out of bounds
17+
if i >= m or j >= n:
18+
return 0
19+
20+
# Memoization
21+
if dp[i][j][dir] != -1:
22+
return dp[i][j][dir]
23+
24+
res = 0
25+
26+
# If current cell is a mirror
27+
if grid[i][j] == 1:
28+
if dir == 0: # Came from left, reflect down
29+
res = dfs(i + 1, j, 1)
30+
else: # dir == 1, came from above, reflect right
31+
res = dfs(i, j + 1, 0)
32+
else:
33+
# Not a mirror, can move both right and down
34+
# Move right
35+
res = (res + dfs(i, j + 1, 0)) % MOD
36+
# Move down
37+
res = (res + dfs(i + 1, j, 1)) % MOD
38+
39+
dp[i][j][dir] = res
40+
return res
41+
42+
# Start at (0,0). Since grid[0][0] is always 0 (not a mirror), we can start with dir=0
43+
# The dir parameter represents the direction we came from, but at start it's arbitrary
44+
# Since (0,0) is not a mirror, we'll try both moves anyway
45+
return dfs(0, 0, 0) % MOD

solutions/3669/01.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class Solution:
2+
def minDifference(self, n: int, k: int) -> List[int]:
3+
# Find all divisors of n
4+
divisors = []
5+
i = 1
6+
while i * i <= n:
7+
if n % i == 0:
8+
divisors.append(i)
9+
if i != n // i:
10+
divisors.append(n // i)
11+
i += 1
12+
divisors.sort()
13+
14+
res = None
15+
min_diff = float('inf')
16+
17+
def dfs(start, picked, prod, path):
18+
nonlocal res, min_diff
19+
20+
if picked == k:
21+
if prod == n:
22+
diff = max(path) - min(path)
23+
if diff < min_diff:
24+
min_diff = diff
25+
res = path[:]
26+
return
27+
28+
if prod > n:
29+
return
30+
31+
for i in range(start, len(divisors)):
32+
divisor = divisors[i]
33+
if prod * divisor > n:
34+
break
35+
path.append(divisor)
36+
dfs(i, picked + 1, prod * divisor, path)
37+
path.pop()
38+
39+
dfs(0, 0, 1, [])
40+
return res
41+

0 commit comments

Comments
 (0)