Skip to content

Commit 14af3ee

Browse files
Merge pull request #103 from romankurnovskii/problems-1523-448-560-581-617-973-981-1029-1167-1249-1304-1337-1423-1528-1584-1672
Add solutions and explanations for problems 1523, 448, 560, 581, 617, 973, 981, 1029, 1167, 1249, 1304, 1337, 1423, 1528, 1584, 1672
2 parents dbf06d8 + fead2fa commit 14af3ee

File tree

23 files changed

+1249
-0
lines changed

23 files changed

+1249
-0
lines changed

explanations/1029/en.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
7+
- **Input Size:** `2 <= costs.length <= 100`, and `costs.length` is even.
8+
- **Value Range:** `1 <= aCost_i, bCost_i <= 1000`.
9+
- **Time Complexity:** O(n log n) where n is the number of people. Sorting takes O(n log n) time.
10+
- **Space Complexity:** O(1) - we sort in place.
11+
- **Edge Case:** If there are only 2 people, send one to each city.
12+
13+
**1.2 High-level approach:**
14+
15+
The goal is to minimize the total cost of sending exactly n people to city A and n people to city B. The key insight is to sort people by the difference `(costA - costB)`. People with a large negative difference (much cheaper to go to A) should go to A, and people with a large positive difference (much cheaper to go to B) should go to B.
16+
17+
![Visualization showing how sorting by cost difference helps determine optimal city assignment]
18+
19+
**1.3 Brute force vs. optimized strategy:**
20+
21+
- **Brute Force:** Try all possible ways to assign n people to city A and n to city B. This takes O(C(2n, n)) time, which is exponential.
22+
- **Optimized Strategy:** Sort by the difference `(costA - costB)`, send the first n to city A and the rest to city B. This takes O(n log n) time.
23+
- **Why it's better:** The greedy approach of sorting by difference ensures we minimize the total cost by prioritizing people who save the most by going to their preferred city.
24+
25+
**1.4 Decomposition:**
26+
27+
1. Sort the costs array by the difference `(costA - costB)`.
28+
2. Send the first n people (smallest differences, meaning cheaper to go to A) to city A.
29+
3. Send the remaining n people (largest differences, meaning cheaper to go to B) to city B.
30+
4. Return the total cost.
31+
32+
### Steps (The "How")
33+
34+
**2.1 Initialization & Example Setup:**
35+
36+
Let's use the example: `costs = [[10, 20], [30, 200], [400, 50], [30, 20]]`.
37+
38+
We need to send 2 people to city A and 2 to city B (n = 2).
39+
40+
**2.2 Start Checking:**
41+
42+
Sort by difference `(costA - costB)`.
43+
44+
**2.3 Trace Walkthrough:**
45+
46+
| Person | costA | costB | Difference (costA - costB) | Sorted order | Assignment |
47+
|--------|-------|-------|---------------------------|--------------|------------|
48+
| 0 | 10 | 20 | -10 | 1 | City A |
49+
| 1 | 30 | 200 | -170 | 0 | City A |
50+
| 2 | 400 | 50 | 350 | 3 | City B |
51+
| 3 | 30 | 20 | 10 | 2 | City B |
52+
53+
After sorting by difference:
54+
- Person 1: difference = -170 (cheapest to go to A) -> City A, cost = 30
55+
- Person 0: difference = -10 (cheaper to go to A) -> City A, cost = 10
56+
- Person 3: difference = 10 (cheaper to go to B) -> City B, cost = 20
57+
- Person 2: difference = 350 (cheapest to go to B) -> City B, cost = 50
58+
59+
**2.4 Increment and Loop:**
60+
61+
Calculate total: 30 + 10 + 20 + 50 = 110.
62+
63+
**2.5 Return Result:**
64+
65+
Return `res = 110` - the minimum total cost.
66+

explanations/1167/en.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
7+
- **Input Size:** `1 <= sticks.length <= 10^4`, `1 <= sticks[i] <= 10^4`.
8+
- **Time Complexity:** O(n log n) where n is the number of sticks. Each heap operation takes O(log n) and we perform O(n) operations.
9+
- **Space Complexity:** O(n) for the heap.
10+
- **Edge Case:** If there's only one stick, return 0 (no connection needed).
11+
12+
**1.2 High-level approach:**
13+
14+
The goal is to minimize the total cost of connecting all sticks, where the cost of connecting two sticks is the sum of their lengths. The key insight is to always connect the two shortest sticks first (greedy approach). This minimizes the cost because shorter sticks are used fewer times in the total sum. We use a min-heap to efficiently get the two shortest sticks at each step.
15+
16+
![Visualization showing how connecting shortest sticks first minimizes total cost, with a heap maintaining the shortest sticks]
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Try all possible orders of connecting sticks. This is exponential and impractical.
21+
- **Optimized Strategy:** Use a min-heap to always connect the two shortest sticks. This greedy approach is optimal.
22+
- **Why it's better:** The greedy approach ensures that shorter sticks (which contribute less) are used in later connections, minimizing the total cost. This is similar to Huffman coding.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Convert the sticks array into a min-heap.
27+
2. While there's more than one stick in the heap:
28+
- Pop the two shortest sticks.
29+
- Calculate the cost of connecting them (sum of their lengths).
30+
- Add the cost to the total.
31+
- Push the merged stick (sum) back into the heap.
32+
3. Return the total cost.
33+
34+
### Steps (The "How")
35+
36+
**2.1 Initialization & Example Setup:**
37+
38+
Let's use the example: `sticks = [2, 4, 3]`.
39+
40+
We initialize:
41+
- Heap: `[2, 3, 4]` (min-heap)
42+
- `res = 0`
43+
44+
**2.2 Start Checking:**
45+
46+
Connect sticks until only one remains.
47+
48+
**2.3 Trace Walkthrough:**
49+
50+
| Step | Heap before | Pop 1 | Pop 2 | Cost | New stick | Heap after | Total cost |
51+
|------|-------------|-------|-------|------|-----------|------------|------------|
52+
| 1 | [2, 3, 4] | 2 | 3 | 5 | 5 | [4, 5] | 5 |
53+
| 2 | [4, 5] | 4 | 5 | 9 | 9 | [9] | 5 + 9 = 14 |
54+
55+
Detailed steps:
56+
- Step 1: Pop 2 and 3, cost = 2 + 3 = 5. Push 5 back. Heap: [4, 5]. Total: 5.
57+
- Step 2: Pop 4 and 5, cost = 4 + 5 = 9. Push 9 back. Heap: [9]. Total: 5 + 9 = 14.
58+
- Only one stick remains, so we're done.
59+
60+
**2.4 Increment and Loop:**
61+
62+
Continue until heap size is 1.
63+
64+
**2.5 Return Result:**
65+
66+
Return `res = 14` - the minimum total cost to connect all sticks.
67+

explanations/1249/en.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
7+
- **Input Size:** `1 <= s.length <= 10^5`.
8+
- **Characters:** `s[i]` is either '(', ')', or a lowercase English letter.
9+
- **Time Complexity:** O(n) - we make two passes through the string.
10+
- **Space Complexity:** O(n) - we use a stack and a set to track indices to remove.
11+
- **Edge Case:** If the string has no parentheses, return the original string. If all parentheses are invalid, return only the letters.
12+
13+
**1.2 High-level approach:**
14+
15+
The goal is to remove the minimum number of parentheses to make the string valid. We use a two-pass approach: first, identify invalid parentheses using a stack, then build the result string by skipping those indices.
16+
17+
![Visualization showing how a stack is used to match parentheses and identify invalid ones that need to be removed]
18+
19+
**1.3 Brute force vs. optimized strategy:**
20+
21+
- **Brute Force:** Try all possible combinations of removing parentheses and check validity. This is exponential.
22+
- **Optimized Strategy:** Use a stack to identify unmatched parentheses in one pass, then build the result in a second pass. This takes O(n) time.
23+
- **Why it's better:** The stack-based approach efficiently identifies invalid parentheses in linear time, which is optimal for this problem.
24+
25+
**1.4 Decomposition:**
26+
27+
1. First pass: Use a stack to track opening parentheses indices.
28+
2. When encountering ')', if stack is empty, mark this ')' for removal. Otherwise, pop from stack (matched pair).
29+
3. After first pass, any remaining indices in the stack are unmatched '(' and should be removed.
30+
4. Second pass: Build the result string by skipping indices marked for removal.
31+
5. Return the result string.
32+
33+
### Steps (The "How")
34+
35+
**2.1 Initialization & Example Setup:**
36+
37+
Let's use the example: `s = "lee(t(c)o)de)"`.
38+
39+
We initialize:
40+
- `stack = []`
41+
- `to_remove = set()`
42+
43+
**2.2 Start Checking:**
44+
45+
First pass: Identify invalid parentheses.
46+
47+
**2.3 Trace Walkthrough:**
48+
49+
| Step | char | index | Action | stack | to_remove |
50+
|------|------|-------|--------|-------|-----------|
51+
| 1 | 'l' | 0 | Letter, skip | [] | {} |
52+
| 2 | 'e' | 1 | Letter, skip | [] | {} |
53+
| 3 | 'e' | 2 | Letter, skip | [] | {} |
54+
| 4 | '(' | 3 | Push index 3 | [3] | {} |
55+
| 5 | 't' | 4 | Letter, skip | [3] | {} |
56+
| 6 | '(' | 5 | Push index 5 | [3, 5] | {} |
57+
| 7 | 'c' | 6 | Letter, skip | [3, 5] | {} |
58+
| 8 | ')' | 7 | Pop from stack | [3] | {} |
59+
| 9 | 'o' | 8 | Letter, skip | [3] | {} |
60+
| 10 | ')' | 9 | Pop from stack | [] | {} |
61+
| 11 | 'd' | 10 | Letter, skip | [] | {} |
62+
| 12 | 'e' | 11 | Letter, skip | [] | {} |
63+
| 13 | ')' | 12 | Stack empty, mark for removal | [] | {12} |
64+
65+
After first pass:
66+
- `stack = []` (all '(' were matched)
67+
- `to_remove = {12}` (unmatched ')')
68+
69+
**2.4 Increment and Loop:**
70+
71+
Second pass: Build result string, skipping index 12.
72+
73+
| Index | char | In to_remove? | Add to result? | Result so far |
74+
|-------|------|---------------|----------------|---------------|
75+
| 0 | 'l' | No | Yes | "l" |
76+
| 1 | 'e' | No | Yes | "le" |
77+
| 2 | 'e' | No | Yes | "lee" |
78+
| 3 | '(' | No | Yes | "lee(" |
79+
| 4 | 't' | No | Yes | "lee(t" |
80+
| 5 | '(' | No | Yes | "lee(t(" |
81+
| 6 | 'c' | No | Yes | "lee(t(c" |
82+
| 7 | ')' | No | Yes | "lee(t(c)" |
83+
| 8 | 'o' | No | Yes | "lee(t(c)o" |
84+
| 9 | ')' | No | Yes | "lee(t(c)o)" |
85+
| 10 | 'd' | No | Yes | "lee(t(c)o)d" |
86+
| 11 | 'e' | No | Yes | "lee(t(c)o)de" |
87+
| 12 | ')' | Yes | No | "lee(t(c)o)de" |
88+
89+
**2.5 Return Result:**
90+
91+
Return `res = "lee(t(c)o)de"` - the valid string after removing the minimum number of parentheses.
92+

explanations/1304/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+
**1.1 Constraints & Complexity:**
6+
7+
- **Input Size:** `1 <= n <= 1000`.
8+
- **Time Complexity:** O(n) - we iterate n/2 times to create pairs.
9+
- **Space Complexity:** O(n) - we create an array of size n.
10+
- **Edge Case:** If n = 1, return [0]. If n = 2, return [1, -1].
11+
12+
**1.2 High-level approach:**
13+
14+
The goal is to create an array of n unique integers that sum to zero. The key insight is to use pairs of positive and negative numbers that cancel each other out. For even n, we can use n/2 pairs. For odd n, we use (n-1)/2 pairs plus 0.
15+
16+
![Visualization showing how positive and negative number pairs cancel out to sum to zero, with 0 added for odd n]
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Try all possible combinations of n unique integers and check if they sum to zero. This is exponential and impractical.
21+
- **Optimized Strategy:** Use symmetric pairs (x, -x) for even n, or pairs plus 0 for odd n. This is simple and efficient.
22+
- **Why it's better:** The symmetric pair approach guarantees a sum of zero and is straightforward to implement in O(n) time.
23+
24+
**1.4 Decomposition:**
25+
26+
1. For each integer from 1 to n/2, add both the positive and negative versions to the result.
27+
2. If n is odd, add 0 to the result.
28+
3. Return the result array.
29+
30+
### Steps (The "How")
31+
32+
**2.1 Initialization & Example Setup:**
33+
34+
Let's use the example: `n = 5`.
35+
36+
We initialize:
37+
- `res = []`
38+
39+
**2.2 Start Checking:**
40+
41+
Create pairs of positive and negative numbers.
42+
43+
**2.3 Trace Walkthrough:**
44+
45+
| Step | i | Positive number | Negative number | res after step |
46+
|------|---|-----------------|------------------|----------------|
47+
| 1 | 1 | 1 | -1 | [1, -1] |
48+
| 2 | 2 | 2 | -2 | [1, -1, 2, -2] |
49+
50+
After creating pairs:
51+
- We have 2 pairs: [1, -1, 2, -2]
52+
- Since n = 5 is odd, we need to add 0
53+
- Final: [1, -1, 2, -2, 0]
54+
55+
**2.4 Increment and Loop:**
56+
57+
Continue until we've created n/2 pairs.
58+
59+
**2.5 Return Result:**
60+
61+
Return `res = [1, -1, 2, -2, 0]` - an array of 5 unique integers that sum to zero (1 + (-1) + 2 + (-2) + 0 = 0).
62+

explanations/1337/en.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
7+
- **Input Size:** `2 <= n, m <= 100`, `1 <= k <= m`.
8+
- **Time Complexity:** O(m * n + m log m) where m is the number of rows. We count soldiers in each row (O(m * n)), then sort (O(m log m)).
9+
- **Space Complexity:** O(m) - we store strength and index for each row.
10+
- **Edge Case:** If k = m, return all row indices. If all rows have the same strength, return the first k indices.
11+
12+
**1.2 High-level approach:**
13+
14+
The goal is to find the k weakest rows, where weakness is determined by the number of soldiers (1s) in the row, with row index as a tiebreaker. We calculate the strength (number of soldiers) for each row, sort by strength (and index as tiebreaker), and return the first k indices.
15+
16+
![Visualization showing a matrix with soldiers (1s) and civilians (0s), with rows sorted by strength from weakest to strongest]
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** For each row, count soldiers, then sort all rows. This is the same as the optimized approach.
21+
- **Optimized Strategy:** Count soldiers for each row, store (strength, index) pairs, sort by strength then index, return first k indices.
22+
- **Why it's better:** By storing (strength, index) pairs and sorting once, we efficiently find the k weakest rows in O(m log m) time after counting.
23+
24+
**1.4 Decomposition:**
25+
26+
1. For each row, count the number of soldiers (1s).
27+
2. Store (strength, row_index) pairs in a list.
28+
3. Sort the list by strength (ascending), then by index (ascending) as tiebreaker.
29+
4. Extract the first k indices from the sorted list.
30+
5. Return the list of k weakest row indices.
31+
32+
### Steps (The "How")
33+
34+
**2.1 Initialization & Example Setup:**
35+
36+
Let's use the example:
37+
```
38+
mat = [[1,1,0,0,0],
39+
[1,1,1,1,0],
40+
[1,0,0,0,0],
41+
[1,1,0,0,0],
42+
[1,1,1,1,1]]
43+
k = 3
44+
```
45+
46+
**2.2 Start Checking:**
47+
48+
Calculate strength for each row.
49+
50+
**2.3 Trace Walkthrough:**
51+
52+
| Row | Row index | Soldiers count | Strength | (strength, index) |
53+
|-----|-----------|----------------|----------|-------------------|
54+
| [1,1,0,0,0] | 0 | 2 | 2 | (2, 0) |
55+
| [1,1,1,1,0] | 1 | 4 | 4 | (4, 1) |
56+
| [1,0,0,0,0] | 2 | 1 | 1 | (1, 2) |
57+
| [1,1,0,0,0] | 3 | 2 | 2 | (2, 3) |
58+
| [1,1,1,1,1] | 4 | 5 | 5 | (5, 4) |
59+
60+
After sorting by (strength, index):
61+
- (1, 2) - weakest
62+
- (2, 0) - second weakest
63+
- (2, 3) - third weakest (tie broken by index)
64+
- (4, 1)
65+
- (5, 4)
66+
67+
**2.4 Increment and Loop:**
68+
69+
Extract first k = 3 indices: [2, 0, 3]
70+
71+
**2.5 Return Result:**
72+
73+
Return `res = [2, 0, 3]` - the indices of the 3 weakest rows.
74+

0 commit comments

Comments
 (0)