Skip to content

Commit 09f3f86

Browse files
My solution to 2134
1 parent 4497928 commit 09f3f86

File tree

1 file changed

+110
-2
lines changed

1 file changed

+110
-2
lines changed

problems/2134/jeremymanning.md

+110-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,119 @@
11
# [Problem 2134: Minimum Swaps to Group All 1's Together II](https://leetcode.com/problems/minimum-swaps-to-group-all-1s-together-ii/description/?envType=daily-question)
22

33
## Initial thoughts (stream-of-consciousness)
4+
- I think there are two steps to solving this:
5+
- First, we have to detect when a block of 1s is surrounded (on either side) by 0s. If that happens, we need to perform swaps to "fill in" the holes
6+
- Second, we (might?) need to account for the circularity property.
7+
- I'm not fully certain that this matters, though, if the way we're detecting "ungrouped 1s" is to look for blocks of 1s that are surrounded on *both* sides by 0s. E.g., for the third provided example (`nums = [1, 1, 0, 0, 1]`), neither block of 1s would be in need of filling in, since neither has 0s on *both* sides.
8+
- So...maybe we just need to:
9+
- Identify islands of 1s
10+
- Count up the numbers of 0s in between (this tells us how many swaps are needed)
11+
- I suspect we could solve this in $O(n)$ time if we just go from left to right through `nums`. We need to keep track of:
12+
- The `count` of the number of swaps needed (initialize to 0)
13+
- Whether we've encountered 0s yet
14+
- Whether we've encountered 1s after encountering 0s
15+
- Then we keep *count* of any subsequent 0s we see. This is important to track since it might get added to `count`.
16+
- If we encounter 1s after that, we'll need to add the temporary count to `count`. Then we reset all of our "state" flags.
417

518
## Refining the problem, round 2 thoughts
19+
- Start by initializing:
20+
- `swaps = 0` (track the minimum number of swaps needed)
21+
- `leadingZeros = False`: have we encountered 0s yet?
22+
- `onesAfterLeadingZeros = False`: have we encountered 1s after encountering (leading) 0s?
23+
- `zerosAfterOnes = False`: have we encountered 0s after encountering 1s (after encountering 0s)? Once this turns to `True` we need to start a temporary counter (also initialize `temp_count = 0`).
24+
- `onesAfterTrailingZeros = False`: have we encountered 1s after the 0s that came after 1s that came after 0s? If we hit this condition, we add the temprary count to `swaps` and reset all flags.
25+
- Let's loop through `nums`. The current element is `i`
26+
- If `i == 0`:
27+
- If `not leadingZeros`:
28+
- `leadingZeros = True`
29+
- Else if `onesAfterLeadingZeros`:
30+
- `temp_count += 1`
31+
- `zerosAfterOnes = True`
32+
- If `i == 1`:
33+
- If `zerosAfterOnes`:
34+
- `onesAfterTrailingZeros = True` -- note: we may not actually need to track this...
35+
- `leadingZeros, onesAfterLeadingZeros, zerosAfterOnes = False, False, False`
36+
- `swaps += temp_count`
37+
- `temp_count = 0`
38+
- Else if `leadingZeros`:
39+
- `onesAfterLeadingZeros = True`
40+
- Now return `swaps`
41+
- Let's implement this...
642

743
## Attempted solution(s)
844
```python
9-
class Solution: # paste your code here!
10-
...
45+
class Solution:
46+
def minSwaps(self, nums: List[int]) -> int:
47+
swaps, tmp_count = 0, 0
48+
leadingZeros, onesAfterLeadingZeros, zerosAfterOnes = False, False, False
49+
50+
for i in nums:
51+
if i == 0:
52+
if not leadingZeros:
53+
leadingZeros = True
54+
elif onesAfterLeadingZeros:
55+
tmp_count += 1
56+
zerosAfterOnes = True
57+
if i == 1:
58+
if zerosAfterOnes:
59+
leadingZeros, onesAfterLeadingZeros, zerosAfterOnes = False, False, False
60+
swaps += tmp_count
61+
tmp_count = 0
62+
elif leadingZeros:
63+
onesAfterLeadingZeros = True
64+
65+
return swaps
66+
```
67+
- All given test cases pass
68+
- Let's make up some new arrays with random 0/1 sequences:
69+
- `nums = [0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0]`: wrong answer! So...what's going on? Let's maybe try with a shorter random sequence:
70+
- `nums = [1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0]`: eh...that passes. Another?
71+
- `nums = [0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]`: that passes too... 🤔
72+
- `nums = [1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0]`: this one fails too! This gives me an idea...
73+
- `nums = [0, 1, 1, 0, 0, 1, 0, 1]`: great, this fails too. So I think I know what's happening: my heuristic for counting the minimum number of swaps doesn't work in all cases. E.g., if we swap positions 0 and 5 (producing the array `[1, 1, 1, 0, 0, 0, 0, 1]`), the new array is now OK (all 1s are grouped, given the circularity property). So we need a different approach for solving this. Back to the drawing board...
74+
75+
## Another round of brainstorming...
76+
- How about thinking up some edge cases and other things we know about the problem:
77+
- If the elements are all 0s or all 1s, the minimum number of swaps is 0 (no calculations needed)
78+
- The length of the final contiguous block of 1s is equal to the number of 1s in `nums`
79+
- The final block could start at any position (note: maybe this is a sliding windows problem?). And we need to account for the possibility that the block could start near the end of the array and wrap around to the beginning (leveraging the circular property of `nums`). Wrapping is easy to simulate; if we ever go "past" the end of the array we can find the equivalent "circularly shifted" position using `nums[i % len(nums)]`
80+
- For a given "attempt" at making the current "block" into a contiguous block of 1s, the number of swaps needed is equal to the number of 0s in that block
81+
- What if we do something like this:
82+
- Count up the total number of 1s. This is simply `n = sum(nums)`
83+
- Looping through each possible start (`i`) of a sliding window of length `n`, count up the number of 0s. Whichever window has the fewest 0s (let's say there are `x` 0s in that window) tells us that we need `x` swaps to make a contiguous block of 1s starting at position `i`.
84+
- Things to solve:
85+
- How do we deal with the circular property of `nums`? We can loop up to an additional `n` elements *past* the `len(nums)` (or...maybe `n - 1`?) and use circular indexing instead of absolute indexing.
86+
- Can we count the numbers of zeros efficiently? This will be an $O(n^2)$ algorithm if we "re-count" the 0s from scratch for every sliding window. But (after the very first window) we actually know that the count will only change by a max of 2:
87+
- If the first element of the previous window was a 0, the new count decrements by 1
88+
- If the last element of the current window is a 0, the new count increments by 1
89+
- Let's put this all together...
90+
91+
## Next attempted solution
92+
```python
93+
class Solution:
94+
def minSwaps(self, nums: List[int]) -> int:
95+
n = sum(nums)
96+
97+
# compute number of 0s in "first" sliding window
98+
n_zeros = n - sum(nums[:n])
99+
100+
# initialize the minimum number of swaps to n_zeros. then loop through the array (using circular indexing)
101+
swaps = n_zeros
102+
for i in range(1, len(nums) + n):
103+
# update n_zeros
104+
if nums[(i - 1) % len(nums)] == 0:
105+
n_zeros -= 1
106+
if nums[(i + n - 1) % len(nums)] == 0:
107+
n_zeros += 1
108+
109+
swaps = min(swaps, n_zeros)
110+
111+
return swaps
11112
```
113+
- All test cases + additional cases now pass!
114+
- Submitting...
115+
116+
![Screenshot 2024-08-02 at 12 04 08 AM](https://github.com/user-attachments/assets/7fd8c342-c77b-423e-945c-fcaa6a51c037)
117+
118+
Solved!
119+

0 commit comments

Comments
 (0)