
The Problem
We need to find invalid product IDs in a database. The IDs are numeric, but "invalid" ones are formed by repeating digit patterns. We are given ranges of IDs to check.
Summary
Part | Question | Strategy |
|---|---|---|
1 | Sum IDs where | Split string in half and compare sides. |
2 | Sum IDs where | Check all possible pattern lengths that divide the ID length evenly. |
Part 1: Repeated Exactly Twice
An ID is invalid if it is composed of a pattern repeated exactly twice.
1212(pattern12× 2) → Invalid123123(pattern123× 2) → Invalid123124→ Valid
I initially thought this would be complex, maybe involving string searching or regex. But then I realized for Part 1, since it's exactly twice, the string must be split perfectly in half![first half] === [second half]
So I quickly implemented that to validate the idea. I'm passing the logic to transform each number in a range to `str` but here it is the logic:
Part 2: Repeated At Least Twice
The definition expands: an ID is invalid if it is a pattern repeated at least twice (2, 3, 4... times).
121212(pattern12× 3) → Invalid111111(pattern1× 6) → Invalid
This required refactoring because the simple "split in half" trick doesn't work for 3 or 5 repetitions.
The Refactored Solution
To handle both parts elegantly, we generalize the check. Instead of just checking length / 2, we check all valid "sub-pattern" lengths.
why len % patternLen?
We only need to check pattern lengths that divide the total length evenly. If you have a 6-digit ID, you can't form it by repeating a 4-digit pattern (4 + 4 = 8).
ID:
123456(Length 6)Try Pattern Length 2:
6 % 2 === 0✅ (Could be 3 repeats)Try Pattern Length 4:
6 % 4 !== 0❌ (Cannot divide evenly)
This saves us from checking impossible pattern sizes.
why len / patternLen?
Once we know a pattern length fits, this tells us how many times it must repeat to fill the string.
ID:
121212(Length 6)Pattern:
12(Length 2)Repetitions:
6 / 2 = 3
We then construct the expected string: '12'.repeat(3) and compare it to the original ID.
Bonus: The Mathematical Approach 🧮
While the string-based solution was already working nicely, something was still bothering me... I was sure I could solve this in a purely mathematical way. And also, let's be honest, the performance of the first solution was just erhm...
1. The "Magic" Multipliers (Precomputation)
The key insight is realizing that repeating a number pattern is just arithmetic.
Take the number 121212. It looks like the pattern 12 repeated 3 times. Mathematically, we can decompose it like this:
That 10101 is a multiplier that depends only on:
The Pattern Length (2 digits)
The Repetition Count (3 times)
If we pre-calculate these multipliers for all possible lengths and repetitions, we can check if a number is valid just by doing ID % multiplier === 0.
We build a lookup table PATTERN_MULTIPLIERS[length][repetitions] at startup:
2. Bitwise Optimization?? (>> 1)
You might spot this line in the check function:
What is >> 1? It's the Bitwise Right Shift operator. Shifting binary digits right by 1 position is mathematically equivalent to integer division by 2 (dropping the remainder).
6 (binary 110) >> 1→3 (binary 011)5 (binary 101) >> 1→2 (binary 010)
Why divide by 2?
By definition, a "repeating pattern" must appear at least twice. Therefore, a single pattern instance cannot be longer than half the total digits.
If ID has 6 digits, the pattern can be at most 3 digits long (
123123).A 4-digit pattern (
1234) would need 8 digits to repeat twice (12341234).
Using >> 1 is just a tiny bit faster 🏎️ and cooler 🤓 than Math.floor(totalDigits / 2).
3. The Check
Now checking an ID becomes purely numeric. We verify if the ID is divisible by any valid multiplier for its length.
Performance Comparison
Solution | Part 1 | Part 2 |
|---|---|---|
String-based ( | 101.67ms | 207.34ms |
Mathematical ( | 32.58ms | 41.21ms |
Speedup | ~3.1x | ~5x |
The mathematical approach is drastically faster because it avoids all string allocations and parsing, relying solely on CPU-friendly integer arithmetic.