Skip to content

Commit

Permalink
Merge pull request #17 from paxtonfitzpatrick/main
Browse files Browse the repository at this point in the history
solutions and notes for problems 1518 and 1550
  • Loading branch information
jeremymanning authored Jul 7, 2024
2 parents d16199c + b05a483 commit ac70ccd
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 0 deletions.
54 changes: 54 additions & 0 deletions problems/1518/paxtonfitzpatrick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# [Problem 1518: Water Bottles](https://leetcode.com/problems/water-bottles)

## Initial thoughts (stream-of-consciousness)

- seems pretty simple...
- when exchanging empties for fulls, will need to use floor division to figure out how many fulls we can get and also modulo to figure out how many empties couldn't be echanged
- also need to keep track of unexchanged empties in case we can accumulate enough for another full (`numExchange`)
- stop when we have no more fulls and not enough empties to exchange for a full
- I think I'll structure this as a `while` loop where each time through the loop we can drink fulls and/or exchange empties
- There's probably a more efficient way to do this without looping, where we calculate everything from the initial values (so, $O(1)$), but this seems like an easier place to start.
- I bet there's also a way to set this up recursively, which could be interesting, but unlikely to be optimal

## Refining the problem, round 2 thoughts

- Version 2: I noticed some small tweaks I could make to the logic that I think would speed up the `while` loop version slightly
- note: key to not having to modify `n_empties` twice each iteration is updating `n_fulls` and `n_empties` simultaneously. New values for both variables depend on the old value of the other, so updating both on the same line ensures both calculations use the "old" values. Could also have stored `n_empties + n_fulls` in a temporary variable, but that would've taken a teeny bit more time & memory.

## Attempted solution(s)

Version 1:

```python
class Solution:
def numWaterBottles(self, numBottles: int, numExchange: int) -> int:
n_fulls = numBottles
n_empties = 0
n_drank = 0
while True:
n_drank += n_fulls
n_empties += n_fulls
if n_empties < numExchange:
return n_drank
n_fulls = n_empties // numExchange
n_empties = n_empties % numExchange
```

![Version 1 results](https://github.com/paxtonfitzpatrick/leetcode-solutions/assets/26118297/08d502ae-a947-49fc-89a9-fb578ea937a6)

Version 2:

```python
class Solution:
def numWaterBottles(self, numBottles: int, numExchange: int) -> int:
n_drank = numBottles
n_fulls = numBottles // numExchange
n_empties = numBottles % numExchange
while n_fulls > 0:
n_drank += n_fulls
n_fulls, n_empties = (n_empties + n_fulls) // numExchange, (n_empties + n_fulls) % numExchange
return n_drank
```

![Version 2 results](https://github.com/paxtonfitzpatrick/leetcode-solutions/assets/26118297/ba0ca272-8761-4cc5-9c2f-9deb12d40361)
Appears to be slower than version 1, but probably just due to random variation runtime of the test cases. Pretty confident that in reality, version 2 is marginally faster.
54 changes: 54 additions & 0 deletions problems/1550/paxtonfitzpatrick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# [Problem 1550: Three Consecutive Odds](https://leetcode.com/problems/three-consecutive-odds/description/)

## Initial thoughts (stream-of-consciousness)

- seems like a pretty easy one
- need to track the number of consecutive odds
- if counter ever reaches 3, return True
- if we encounter an even number, reset counter to 0

## Refining the problem

- I bet this will ultimately be slower, but I can think of a kinda fun, roundabout way solve this:
- convert `arr` to a string
- use regular expression to search for 3 consecutive occurrences of values that end in 1, 3, 5, 7, or 9, separated by a command and space
- we know that all values are <= 1000, and 1000 is an even number, so all odd numbers will have 1, 2, or 3 digits -- so for each number we need to match 0, 1, or 2 digits of any value, followed by an odd digit: `[0-9]{0,2}[13579]`
- base pattern would be `(?:[0-9]{0,2}[13579], ){3}` but:
- this would fail for the last 3 values in the list because there's no trailing comma. So instead use `(?:[0-9]{0,2}[13579], ){2}[0-9]{0,2}[13579]`
- Also for the first of the 3 odd numbers, we should only try to match the final digit rather than all digits, otherwise the search will be slower because the pattern will initially match all values in the list and waste a bunch of time backtracking. So instead use `[13579], [0-9]{0,2}[13579], [0-9]{0,2}[13579]` or `[13579](?:, [0-9]{0,2}[13579]){2}`

- `re.search` returns a `Match` object as soon as it encounters the first match, and `None` if no match is found. So we should be able to just `bool()` the result and return it.
- I wonder if compiling the pattern upfront would be faster than using using the module-level functions... depends on how leetcode runs the test cases, I guess. If I compile the expression in the class body outside the method definition, and all test case runs reuse the same instance of the class, then it should save time. Otherwise, it probably wouldn't. Maybe I'll just compile it in the global namespace and see.

## Attempted solution(s)

```python
class Solution:
def threeConsecutiveOdds(self, arr: List[int]) -> bool:
if len(arr) < 3:
return False

consec_odds = 0
for val in arr:
if val % 2:
if consec_odds == 2:
return True
consec_odds += 1
else:
consec_odds = 0
return False
```

![](https://github.com/paxtonfitzpatrick/leetcode-solutions/assets/26118297/c45d108c-b23b-48d8-bc14-13a2cdc0cc06)

```python
from re import compile

pattern = compile(r"[13579](?:, [0-9]{0,2}[13579]){2}")

class Solution:
def threeConsecutiveOdds(self, arr: List[int]) -> bool:
return bool(pattern.search(str(arr)))
```

![](https://github.com/paxtonfitzpatrick/leetcode-solutions/assets/26118297/88d07bb2-f252-4797-81e4-33793acaeae4)

0 comments on commit ac70ccd

Please sign in to comment.