A clear explanation of counting good numbers after rotating every digit by 180 degrees.
Problem Restatement
We are given an integer n.
We need to count how many integers x in the range:
1 <= x <= nare good numbers.
A number is good if, after rotating every digit by 180 degrees, we get:
- A valid number.
- A different number from the original.
The valid digit rotations are:
| Digit | Rotates to |
|---|---|
0 | 0 |
1 | 1 |
2 | 5 |
5 | 2 |
6 | 9 |
8 | 8 |
9 | 6 |
Digits 3, 4, and 7 become invalid after rotation.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer n |
| Output | Count of good numbers from 1 to n |
| Valid unchanged digits | 0, 1, 8 |
| Valid changed digits | 2, 5, 6, 9 |
| Invalid digits | 3, 4, 7 |
Function shape:
class Solution:
def rotatedDigits(self, n: int) -> int:
...Examples
Example 1:
n = 10Check numbers from 1 to 10.
The good numbers are:
2, 5, 6, 9Why?
| Number | Rotated | Good? |
|---|---|---|
1 | 1 | No, unchanged |
2 | 5 | Yes |
5 | 2 | Yes |
6 | 9 | Yes |
8 | 8 | No, unchanged |
9 | 6 | Yes |
10 | 10 | No, unchanged |
So the answer is:
4Example 2:
n = 1The only number is 1.
After rotation, it is still 1, so it is not good.
The answer is:
0Example 3:
n = 2The numbers are 1 and 2.
Only 2 is good.
The answer is:
1First Thought: Rotate Every Number
The simplest method is to check every number from 1 to n.
For each number:
- Look at each digit.
- If any digit is invalid, the number is not good.
- Track whether at least one digit changes after rotation.
- If all digits are valid and at least one digit changes, count it.
This is direct and easy to implement.
Key Insight
A number is good exactly when both conditions hold:
| Condition | Meaning |
|---|---|
| No invalid digits | It contains no 3, 4, or 7 |
| At least one changing digit | It contains at least one of 2, 5, 6, or 9 |
Digits 0, 1, and 8 are valid but do not change the number.
So a number like:
101is valid after rotation, but unchanged. It is not good.
A number like:
102is good because 2 rotates to 5.
A number like:
123is invalid because 3 cannot rotate into a digit.
Algorithm
- Initialize
answer = 0. - For each number
xfrom1ton:- Check whether
xis good. - If yes, increment
answer.
- Check whether
- Return
answer.
To check whether a number is good:
- Convert it to a string.
- For each digit:
- If it is in
3,4, or7, returnFalse. - If it is in
2,5,6, or9, mark that the number changes.
- If it is in
- Return whether the number changes.
Correctness
A number is valid after rotation only if every digit has a valid rotated form. The digits with valid rotated forms are exactly 0, 1, 2, 5, 6, 8, and 9. Therefore, if the algorithm sees 3, 4, or 7, it correctly rejects the number.
A valid rotated number is different from the original exactly when at least one digit changes. The digits that change are exactly 2, 5, 6, and 9. The digits 0, 1, and 8 remain the same.
The algorithm counts a number only when it has no invalid digit and has at least one changing digit. These are exactly the conditions for being a good number. Since it checks every number from 1 to n, it returns the correct count.
Complexity
Let d be the number of digits in n.
| Metric | Value | Why |
|---|---|---|
| Time | O(n * d) | We check up to d digits for each number |
| Space | O(d) | String conversion stores the digits of one number |
Since d = O(log n), the time complexity can also be written as:
O(n log n)Implementation
class Solution:
def rotatedDigits(self, n: int) -> int:
changed = {"2", "5", "6", "9"}
invalid = {"3", "4", "7"}
def is_good(x: int) -> bool:
has_changed_digit = False
for digit in str(x):
if digit in invalid:
return False
if digit in changed:
has_changed_digit = True
return has_changed_digit
answer = 0
for x in range(1, n + 1):
if is_good(x):
answer += 1
return answerCode Explanation
We separate the important digit groups:
changed = {"2", "5", "6", "9"}
invalid = {"3", "4", "7"}A number with any invalid digit cannot be good:
if digit in invalid:
return FalseA number with a changing digit may be good:
if digit in changed:
has_changed_digit = TrueAt the end, the number is good only if it had at least one changing digit:
return has_changed_digitThen we check every number in the range:
for x in range(1, n + 1):
if is_good(x):
answer += 1Alternative: Digit State DP
There is also a compact dynamic programming approach.
Classify each number by state:
| State | Meaning |
|---|---|
0 | Valid so far, but unchanged |
1 | Valid and changed |
2 | Invalid |
For a number x, let:
dp[x]describe whether x is invalid, valid unchanged, or valid changed.
We can derive dp[x] from dp[x // 10] and the last digit.
class Solution:
def rotatedDigits(self, n: int) -> int:
status = [0] * (n + 1)
answer = 0
for x in range(1, n + 1):
last = x % 10
rest = x // 10
if last in (3, 4, 7) or status[rest] == 2:
status[x] = 2
elif last in (2, 5, 6, 9) or status[rest] == 1:
status[x] = 1
answer += 1
else:
status[x] = 0
return answerThis has the same linear scan over numbers, but avoids converting each number to a string.
Testing
def run_tests():
s = Solution()
assert s.rotatedDigits(1) == 0
assert s.rotatedDigits(2) == 1
assert s.rotatedDigits(10) == 4
assert s.rotatedDigits(20) == 9
assert s.rotatedDigits(100) == 40
assert s.rotatedDigits(3) == 1
assert s.rotatedDigits(8) == 3
print("all tests passed")
run_tests()Test coverage:
| Test | Why |
|---|---|
n = 1 | Only unchanged valid digit |
n = 2 | First good number appears |
n = 10 | Standard example |
n = 20 | Checks two-digit numbers |
n = 100 | Checks larger range |
n = 3 | Includes first invalid digit |
n = 8 | Mix of changed, unchanged, and invalid digits |