Skip to content

Commit e70407f

Browse files
Merge pull request #122 from romankurnovskii/problems-3178-3179-3212-3213-3244-3245-3276-3277-3307-3309-3340-3341-3371-3372-3404-3405-3436-3438-3464-3465-3493-3494-3523-3524-3552-3553-3582-3584-3615-3617-3646-3648-3677-3679-3725-3729
Add solutions and explanations for problems 3178, 3179, 3212, 3244, 3276
2 parents 69393c9 + 8e3c0b7 commit e70407f

File tree

18 files changed

+823
-2
lines changed

18 files changed

+823
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ scripts/__pycache__/
1212
descriptions/
1313
.venv/
1414
__pycache__/
15+
node_modules/

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npm run format:json

data/book-sets.json

Lines changed: 224 additions & 1 deletion
Large diffs are not rendered by default.

explanations/3178/en.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We have n children numbered 0 to n-1 standing in a line. Child 0 initially holds a ball and passes it right. When the ball reaches either end (child 0 or child n-1), the direction reverses. We need to find which child has the ball after k seconds.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** n is between 2 and 50, k is between 1 and 50. These are small values, so simulation is feasible.
10+
- **Time Complexity:** O(k mod period) where period = 2*(n-1). Since k and n are small, this is effectively O(k) in worst case.
11+
- **Space Complexity:** O(1) - we only use a few variables to track position and direction.
12+
- **Edge Case:** When n=2, the period is 2, so the ball alternates between children 0 and 1.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to simulate the ball passing pattern. The key insight is that the pattern repeats every 2*(n-1) seconds, so we can use modulo arithmetic to reduce k before simulation.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Simulate all k seconds step by step. This is O(k) time.
21+
- **Optimized Strategy:** Recognize that the pattern repeats every 2*(n-1) seconds. Use k % (2*(n-1)) to reduce the simulation time. This is still O(k) in worst case but more efficient for large k values.
22+
- **Optimization:** By using modulo, we avoid simulating redundant cycles when k is much larger than the period.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Calculate the period of repetition: 2 * (n - 1) seconds.
27+
2. Reduce k using modulo: k_mod = k % period.
28+
3. Initialize position at 0 and direction as 1 (right).
29+
4. Simulate k_mod steps: move position by direction, reverse direction at boundaries.
30+
5. Return the final position.
31+
32+
### Steps (The "How")
33+
34+
**2.1 Initialization & Example Setup:**
35+
36+
Let's use the example input: `n = 3`, `k = 5`.
37+
38+
- Period = 2 * (3 - 1) = 2 * 2 = 4
39+
- k_mod = 5 % 4 = 1
40+
- Initial position = 0, direction = 1 (right)
41+
42+
**2.2 Start Simulation:**
43+
44+
We simulate k_mod = 1 step.
45+
46+
**2.3 Trace Walkthrough:**
47+
48+
| Step | Position | Direction | Action |
49+
|------|----------|-----------|--------|
50+
| 0 | 0 | 1 (right) | Start at child 0 |
51+
| 1 | 1 | 1 (right) | Move right to child 1 |
52+
53+
**2.4 Increment and Loop:**
54+
55+
After k_mod steps, we have the final position.
56+
57+
**2.5 Return Result:**
58+
59+
The final position is 1, so child 1 has the ball after k=5 seconds. This matches the example where after 5 seconds, the ball is at child 1.
60+

explanations/3179/en.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We start with an array of n integers, all set to 1. After each second, we update each element to be the sum of all its preceding elements plus the element itself. We need to find the value at index n-1 after k seconds.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** n and k are both between 1 and 1000. The values can grow very large, so we need modulo 10^9 + 7.
10+
- **Time Complexity:** O(n \* k) - we iterate k times, and for each iteration, we process n-1 elements.
11+
- **Space Complexity:** O(n) - we maintain an array of size n.
12+
- **Edge Case:** When k=0, the array remains [1,1,...,1], so the result is 1.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to apply prefix sum operation k times. Each second, we compute the prefix sum of the current array, which becomes the new array for the next second.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Simulate k seconds by computing prefix sums k times. This is O(n \* k) time and O(n) space.
21+
- **Optimized Strategy:** The same approach is already optimal. We can't avoid computing each prefix sum operation.
22+
- **Optimization:** Using modulo arithmetic ensures we don't overflow, and the direct simulation is the most straightforward approach.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Initialize an array of n elements, all set to 1.
27+
2. For k iterations, compute the prefix sum of the current array.
28+
3. Apply modulo 10^9 + 7 to prevent overflow.
29+
4. Return the value at index n-1.
30+
31+
### Steps (The "How")
32+
33+
**2.1 Initialization & Example Setup:**
34+
35+
Let's use the example input: `n = 4`, `k = 5`.
36+
37+
- Initial array: `[1, 1, 1, 1]`
38+
39+
**2.2 Start Processing:**
40+
41+
We apply prefix sum k=5 times.
42+
43+
**2.3 Trace Walkthrough:**
44+
45+
| Second | Array State | Action |
46+
| ------ | -------------- | ----------------------------------------------------------- |
47+
| 0 | [1, 1, 1, 1] | Initial state |
48+
| 1 | [1, 2, 3, 4] | Prefix sum: arr[1]=1+1=2, arr[2]=1+2=3, arr[3]=1+2+3=4 |
49+
| 2 | [1, 3, 6, 10] | Prefix sum: arr[1]=1+2=3, arr[2]=1+3+2=6, arr[3]=1+3+6=10 |
50+
| 3 | [1, 4, 10, 20] | Prefix sum: arr[1]=1+3=4, arr[2]=1+4+3=10, arr[3]=1+4+10=20 |
51+
| 4 | [1, 5, 15, 35] | Prefix sum: arr[1]=1+4=5, arr[2]=1+5+4=15, arr[3]=1+5+15=35 |
52+
| 5 | [1, 6, 21, 56] | Prefix sum: arr[1]=1+5=6, arr[2]=1+6+5=21, arr[3]=1+6+21=56 |
53+
54+
**2.4 Increment and Loop:**
55+
56+
After k iterations, we have the final array state.
57+
58+
**2.5 Return Result:**
59+
60+
The value at index n-1 (index 3) after k=5 seconds is 56, which matches the example output.

explanations/3212/en.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We are given a 2D character matrix where each cell contains 'X', 'Y', or '.'. We need to count submatrices that start at (0,0), contain equal frequency of 'X' and 'Y', and have at least one 'X'.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** Grid dimensions can be up to 1000x1000. We need an efficient approach.
10+
- **Time Complexity:** O(m * n * min(m,n)) - we check all submatrices starting from (0,0), and for each, we check if it contains at least one X, which takes O(m*n) in worst case.
11+
- **Space Complexity:** O(m * n) for the prefix sum matrix.
12+
- **Edge Case:** If the grid has no 'X', the result is 0.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to use prefix sums to efficiently calculate the sum of any submatrix. We convert 'X' to 1, 'Y' to -1, and '.' to 0. A submatrix with sum 0 has equal frequency of X and Y.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** For each submatrix, count X and Y by iterating through all cells. This is O(m^2 * n^2) time.
21+
- **Optimized Strategy:** Use prefix sum to calculate submatrix sum in O(1) time. Check all submatrices starting from (0,0) and verify they contain at least one X. This is O(m * n * min(m,n)) time.
22+
- **Optimization:** Prefix sum allows us to compute submatrix sums efficiently, reducing the time complexity significantly.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Convert characters to numeric values: X=1, Y=-1, .=0.
27+
2. Build a prefix sum matrix for efficient submatrix sum calculation.
28+
3. For each submatrix starting from (0,0), check if it contains at least one X.
29+
4. If it does and the sum is 0 (equal X and Y), increment the result.
30+
31+
### Steps (The "How")
32+
33+
**2.1 Initialization & Example Setup:**
34+
35+
Let's use the example input: `grid = [["X","Y","."],["Y",".","."]]`
36+
37+
- Convert to numeric: X=1, Y=-1, .=0
38+
- Build prefix sum matrix
39+
40+
**2.2 Start Checking:**
41+
42+
We iterate through all possible submatrices starting from (0,0).
43+
44+
**2.3 Trace Walkthrough:**
45+
46+
| Submatrix | Prefix Sum | Has X? | Sum = 0? | Count |
47+
|-----------|------------|--------|----------|-------|
48+
| (0,0) to (0,0) | 1 | Yes | No | 0 |
49+
| (0,0) to (0,1) | 1 + (-1) = 0 | Yes | Yes | 1 |
50+
| (0,0) to (0,2) | 0 + 0 = 0 | Yes | Yes | 2 |
51+
| (0,0) to (1,0) | 1 + (-1) = 0 | Yes | Yes | 3 |
52+
| (0,0) to (1,1) | 1 + (-1) + (-1) + 0 = -1 | Yes | No | 3 |
53+
| (0,0) to (1,2) | -1 + 0 = -1 | Yes | No | 3 |
54+
55+
**2.4 Increment and Loop:**
56+
57+
We continue checking all submatrices and count those that meet the criteria.
58+
59+
**2.5 Return Result:**
60+
61+
The result is 3, which matches the example output. The three valid submatrices are: (0,0)-(0,1), (0,0)-(0,2), and (0,0)-(1,0).
62+

explanations/3244/en.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We have n cities in a line, with initial roads from city i to i+1. We process queries that add new roads, and after each query, we need to find the shortest path length from city 0 to city n-1.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** n can be up to 10^5, and there can be up to 10^5 queries. We need an efficient O(n + q) solution.
10+
- **Time Complexity:** O(n + q) - we initialize the linked list in O(n) and process each query in O(1) amortized time.
11+
- **Space Complexity:** O(n) - we maintain a dictionary representing the linked list structure.
12+
- **Edge Case:** When n=2, there's only one edge, so the path length is always 1.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to model the graph as a linked list. Initially, each city i points to i+1. When we add a road from i to j, we remove all intermediate nodes and create a direct link. The shortest path length equals the number of edges in the linked list.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** After each query, run BFS to find shortest path. This is O(q * n) time.
21+
- **Optimized Strategy:** Model as a linked list and maintain it efficiently. When adding edge (i,j), remove nodes between i and j. The path length is the size of the dictionary. This is O(n + q) time.
22+
- **Optimization:** The linked list approach avoids repeated graph traversals and allows us to update the path structure efficiently.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Initialize a dictionary d where d[i] = i+1 for all i from 0 to n-2 (linked list structure).
27+
2. For each query (i, j), if d[i] < j, remove all nodes between i and j, then set d[i] = j.
28+
3. The shortest path length is the number of edges, which equals len(d).
29+
4. Return the path length after each query.
30+
31+
### Steps (The "How")
32+
33+
**2.1 Initialization & Example Setup:**
34+
35+
Let's use the example input: `n = 5`, `queries = [[2,4],[0,2],[0,4]]`
36+
37+
- Initial dictionary: `{0: 1, 1: 2, 2: 3, 3: 4}`
38+
- Initial path length: 4 (edges: 0→1, 1→2, 2→3, 3→4)
39+
40+
**2.2 Start Processing:**
41+
42+
We process each query and update the linked list.
43+
44+
**2.3 Trace Walkthrough:**
45+
46+
| Query | Dictionary Before | Action | Dictionary After | Path Length |
47+
|-------|-------------------|--------|------------------|-------------|
48+
| [2,4] | {0:1, 1:2, 2:3, 3:4} | Remove node 3, set d[2]=4 | {0:1, 1:2, 2:4} | 3 |
49+
| [0,2] | {0:1, 1:2, 2:4} | Remove node 1, set d[0]=2 | {0:2, 2:4} | 2 |
50+
| [0,4] | {0:2, 2:4} | Remove node 2, set d[0]=4 | {0:4} | 1 |
51+
52+
**2.4 Increment and Loop:**
53+
54+
After processing all queries, we have the final path lengths.
55+
56+
**2.5 Return Result:**
57+
58+
The result is [3, 2, 1], which matches the example output. The path lengths decrease as we add shortcuts that bypass intermediate cities.
59+

explanations/3276/en.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We need to select one or more cells from a grid such that no two selected cells are in the same row, and all selected values are unique. We want to maximize the sum of selected cell values.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** Grid dimensions are at most 10x10, and values are between 1 and 100. The small size allows for exponential solutions.
10+
- **Time Complexity:** O(V * R^V) where V is the number of unique values and R is the number of rows. With memoization, this is more efficient in practice.
11+
- **Space Complexity:** O(V * 2^R) for memoization - we store states based on which rows are used and which values we've processed.
12+
- **Edge Case:** If all values in a row are the same, we can only select one value from that row.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to use dynamic programming with memoization. We process values in descending order (greedy approach), and for each value, we decide which row to select it from (if any), ensuring we don't violate constraints.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Try all combinations of cells, checking constraints. This is exponential and inefficient.
21+
- **Optimized Strategy:** Use DFS with memoization, processing values in descending order. Track which rows have been used and which values we've processed. This reduces redundant computations significantly.
22+
- **Optimization:** Memoization based on (row_set, value_idx) avoids recomputing the same subproblems, and processing in descending order ensures we consider optimal choices first.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Collect all unique values from the grid and sort them in descending order.
27+
2. Create a mapping from each value to the list of rows containing it.
28+
3. Use DFS with memoization: for each value, try selecting it from each available row, or skip it.
29+
4. Track used rows to ensure no two cells from the same row are selected.
30+
5. Return the maximum score achievable.
31+
32+
### Steps (The "How")
33+
34+
**2.1 Initialization & Example Setup:**
35+
36+
Let's use the example input: `grid = [[1,2,3],[4,3,2],[1,1,1]]`
37+
38+
- Unique values (descending): [4, 3, 2, 1]
39+
- Value to rows mapping: {4: [1], 3: [0,1], 2: [0,1], 1: [0,2]}
40+
41+
**2.2 Start DFS:**
42+
43+
We process values in descending order, making optimal choices.
44+
45+
**2.3 Trace Walkthrough:**
46+
47+
| Value | Available Rows | Decision | Used Rows | Score |
48+
|-------|----------------|----------|-----------|-------|
49+
| 4 | [1] | Select from row 1 | {1} | 4 |
50+
| 3 | [0,1] | Row 1 used, select from row 0 | {0,1} | 4+3=7 |
51+
| 2 | [0,1] | Both rows used, skip | {0,1} | 7 |
52+
| 1 | [0,2] | Row 0 used, select from row 2 | {0,1,2} | 7+1=8 |
53+
54+
**2.4 Increment and Loop:**
55+
56+
The DFS explores all possibilities and returns the maximum score.
57+
58+
**2.5 Return Result:**
59+
60+
The maximum score is 8, achieved by selecting values 4 (row 1), 3 (row 0), and 1 (row 2). This matches the example output.
61+

explanations/3573/en.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We are given stock prices for each day and allowed to make at most k transactions. Each transaction can be either a normal transaction (buy then sell) or a short selling transaction (sell then buy back). We must find the maximum profit we can earn.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** We have at most 1000 days (prices.length <= 10^3), and each price is between 1 and 10^9. We can make at most k transactions where k <= prices.length / 2.
10+
- **Time Complexity:** O(n * k) where n is the number of days and k is the maximum number of transactions. We iterate through each day and for each day, we process all k transactions.
11+
- **Space Complexity:** O(k) for storing the DP arrays (res, bought, sold) of size k.
12+
- **Edge Case:** If k is 0, we cannot make any transactions, so the profit is 0.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to maximize profit by choosing the best sequence of transactions (normal or short selling) up to k transactions. We use dynamic programming to track the best profit at each stage of transaction completion.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Try all possible combinations of transactions (normal or short) at each day, which would be exponential in complexity O(2^(n*k)).
21+
- **Optimized Strategy:** Use dynamic programming with three state arrays: `res[j]` tracks best profit after completing j transactions, `bought[j]` tracks best profit when holding a bought position in the j-th transaction, and `sold[j]` tracks best profit when holding a short position in the j-th transaction. This is O(n * k) time and O(k) space.
22+
- **Optimization:** By maintaining separate states for bought and sold positions, we can efficiently track and update the maximum profit at each stage without recalculating all possibilities.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Initialize three arrays: `res` (completed transactions), `bought` (holding bought positions), and `sold` (holding short positions).
27+
2. For each day's price, process transactions from k down to 1 (to avoid using updated values).
28+
3. For each transaction count j, update `res[j]` by completing either a normal transaction (sell) or short transaction (buy back).
29+
4. Update `bought[j-1]` by starting a new normal transaction (buy at current price).
30+
5. Update `sold[j-1]` by starting a new short transaction (sell at current price).
31+
6. Return the maximum value in `res` array.
32+
33+
### Steps (The "How")
34+
35+
**2.1 Initialization & Example Setup:**
36+
37+
Let's use the example input: `prices = [1, 7, 9, 8, 2]`, `k = 2`.
38+
39+
- Initialize `res = [0, 0, 0]` (profit after 0, 1, or 2 completed transactions)
40+
- Initialize `bought = [-inf, -inf]` (best profit when holding bought position in transaction 0 or 1)
41+
- Initialize `sold = [0, 0]` (best profit when holding short position in transaction 0 or 1)
42+
43+
**2.2 Start Processing:**
44+
45+
We iterate through each price in the array, updating our DP states.
46+
47+
**2.3 Trace Walkthrough:**
48+
49+
| Day | Price | Transaction | Action | res | bought | sold |
50+
|-----|-------|-------------|--------|-----|--------|------|
51+
| 0 | 1 | j=2 | Start normal: bought[1] = max(-inf, res[1] - 1) = max(-inf, 0 - 1) = -1 | [0,0,0] | [-inf,-1] | [0,0] |
52+
| 0 | 1 | j=1 | Start normal: bought[0] = max(-inf, res[0] - 1) = max(-inf, 0 - 1) = -1 | [0,0,0] | [-1,-1] | [0,0] |
53+
| 1 | 7 | j=2 | Complete normal: res[2] = max(0, bought[1] + 7) = max(0, -1 + 7) = 6 | [0,0,6] | [-1,-1] | [0,0] |
54+
| 1 | 7 | j=2 | Start normal: bought[1] = max(-1, res[1] - 7) = max(-1, 0 - 7) = -1 | [0,0,6] | [-1,-1] | [0,0] |
55+
| 1 | 7 | j=1 | Complete normal: res[1] = max(0, bought[0] + 7) = max(0, -1 + 7) = 6 | [0,6,6] | [-1,-1] | [0,0] |
56+
| 1 | 7 | j=1 | Start normal: bought[0] = max(-1, res[0] - 7) = max(-1, 0 - 7) = -1 | [0,6,6] | [-1,-1] | [0,0] |
57+
| 2 | 9 | j=2 | Complete normal: res[2] = max(6, bought[1] + 9) = max(6, -1 + 9) = 8 | [0,6,8] | [-1,-1] | [0,0] |
58+
| 2 | 9 | j=2 | Start normal: bought[1] = max(-1, res[1] - 9) = max(-1, 6 - 9) = -1 | [0,6,8] | [-1,-1] | [0,0] |
59+
| 2 | 9 | j=1 | Complete normal: res[1] = max(6, bought[0] + 9) = max(6, -1 + 9) = 8 | [0,8,8] | [-1,-1] | [0,0] |
60+
| 2 | 9 | j=1 | Start normal: bought[0] = max(-1, res[0] - 9) = max(-1, 0 - 9) = -1 | [0,8,8] | [-1,-1] | [0,0] |
61+
| 3 | 8 | j=2 | Complete normal: res[2] = max(8, bought[1] + 8) = max(8, -1 + 8) = 8 | [0,8,8] | [-1,-1] | [0,0] |
62+
| 3 | 8 | j=2 | Start short: sold[1] = max(0, res[1] + 8) = max(0, 8 + 8) = 16 | [0,8,8] | [-1,-1] | [0,16] |
63+
| 3 | 8 | j=1 | Complete normal: res[1] = max(8, bought[0] + 8) = max(8, -1 + 8) = 8 | [0,8,8] | [-1,-1] | [0,16] |
64+
| 3 | 8 | j=1 | Start short: sold[0] = max(0, res[0] + 8) = max(0, 0 + 8) = 8 | [0,8,8] | [-1,-1] | [8,16] |
65+
| 4 | 2 | j=2 | Complete short: res[2] = max(8, sold[1] - 2) = max(8, 16 - 2) = 14 | [0,8,14] | [-1,-1] | [8,16] |
66+
| 4 | 2 | j=1 | Complete short: res[1] = max(8, sold[0] - 2) = max(8, 8 - 2) = 8 | [0,8,14] | [-1,-1] | [8,16] |
67+
68+
**2.4 Increment and Loop:**
69+
70+
After processing all days, we check all values in the `res` array to find the maximum profit.
71+
72+
**2.5 Return Result:**
73+
74+
The maximum value in `res` is 14, which represents the maximum profit achievable with at most 2 transactions. This comes from: normal transaction (buy at 1, sell at 9) = 8, and short transaction (sell at 8, buy back at 2) = 6, total = 14.
75+

0 commit comments

Comments
 (0)