You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Add solution blog post for 2025 day 2
* Fix typos in docs/2025/puzzles/day02.md
Co-authored-by: Merlin Hughes <merlin@merlin.org>
* simplify regular expressions note
* add note about flattening collections
* Add a note about optimizing by generating invalid IDs directly
* add final code section for 2025 day 2
* Update docs/2025/puzzles/day02.md
fix typos in 2025 day 2 post
---------
Co-authored-by: Merlin Hughes <merlin@merlin.org>
Copy file name to clipboardExpand all lines: docs/2025/puzzles/day02.md
+99-2Lines changed: 99 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,11 +2,108 @@ import Solver from "../../../../../website/src/components/Solver.js"
2
2
3
3
# Day 2: Gift Shop
4
4
5
+
by [@stewSquared](https://github.com/stewSquared)
6
+
5
7
## Puzzle description
6
8
7
9
https://adventofcode.com/2025/day/2
8
10
11
+
## Solution Summary
12
+
13
+
Brute force is sufficient here. We test every number in the ranges for invalid IDs.
14
+
15
+
### Part 1
16
+
17
+
First we parse the input string. We collect the range strings by splitting on commas, then we can represent each range with an [Inclusive](https://www.scala-lang.org/api/current/scala/collection/immutable/NumericRange$$Inclusive.html)[`NumericRange`](https://www.scala-lang.org/api/current/scala/collection/immutable/NumericRange.html) from the standard library:
18
+
19
+
```scala
20
+
valranges= input.split(',').map:
21
+
cases"$a-$b"=> a.toLong to b.toLong
22
+
```
23
+
24
+
Next, we need to be able to determine if a particular ID is invalid. We can do this by splitting the string representation of the ID into two parts and comparing them:
25
+
26
+
```scala
27
+
definvalid(id: Long):Boolean=
28
+
vals= id.toString
29
+
val (left, right) = s.splitAt(s.length /2)
30
+
left == right
31
+
```
32
+
33
+
At this point, we can get every ID from the input by flattening our ranges, then we simply filter with `invalid`:
Note that while `Range` acts like a collection, the individual numbers aren't stored in memory, but when an array of ranges is flattened, it's concretized into an array of numbers, so we first convert with `.iterator` to prevent allocating a full `Array` of all the IDs being checked.
40
+
41
+
### Part 2
42
+
43
+
All we need to change is the definition of invalid. Instead of half a string repeated twice, we have a smaller segment of a string repeated multiple times. More specifically, for a proper divisor `d` of the length of the ID `n`, the first `d` characters of the ID are repeated `n/d` times.
44
+
45
+
Our ID strings are short enough that we can filter possible divisors with modulo, cases where `n % d == 0`. We can then check if a segment repeats by "multiplying" the segment, and comparing it to the original ID. Eg., a string like `"123"`, `"123" * 3 == "123123123"`.
46
+
47
+
```scala
48
+
definvalid2(id: Long) =
49
+
vals= id.toString
50
+
valn= s.length
51
+
valdivisors= (1 to n /2).filter(n % _ ==0)
52
+
divisors.exists(d => s.take(d) * (n/d) == s)
53
+
```
54
+
55
+
And now we can use the same line from `ans1` with the updated function:
We can match any sequence of digits using `\d+`. If we place that in a parenthesized group, we can reference it with `\1` to account for repeats. Part 1 looks like so:
This first matches any sequence of digits, then succeeds if that sequence is followed by itself. For part 2, we repeat the `\1` match at least once, using `+`:
While a brute force check of each possible ID works for the provided inputs, an input range could very easily represent gigabytes of Longs. Instead, it's possible to generate invalid IDs directly (eg., start with `123` and multiply by `1001`, `1001001`, etc.). In a solution by [@merlinorg](https://github.com/merlinorg), [such an approach](https://github.com/merlinorg/advent-of-code/blob/789cb88de7e09bc36928b87be685cc95b30e9a4a/src/main/scala/year2025/day02.scala#L30-L42) drops complexity from `O(n)` to `O(sqrt(n))` for part 1.
0 commit comments