An integer x
is a good if after rotating each digit individually by 180 degrees, we get a valid number that is different from x
. Each digit must be rotated - we cannot choose to leave it alone.
A number is valid if each digit remains a digit after rotation. For example:
0
, 1
, and 8
rotate to themselves,2
and 5
rotate to each other (in this case they are rotated in a different direction, in other words, 2
or 5
gets mirrored),6
and 9
rotate to each other, andGiven an integer n
, return the number of good integers in the range [1, n]
.
For example:
n = 10
, the output should be 4. The good numbers in the range [1, 10] are 2, 5, 6, 9.n = 1
, the output should be 0.n = 2
, the output should be 1.What is the most efficient way to implement this function?
A naive solution would be to iterate through all numbers from 1 to n
and check for each number if it is a good number. A number is good if, after rotating each digit individually by 180 degrees, we get a valid number that is different from the original number.
n
.x
, convert it to a string.def is_good(n):
s = str(n)
rotated = ''
for digit in s:
if digit == '0':
rotated += '0'
elif digit == '1':
rotated += '1'
elif digit == '2':
rotated += '5'
elif digit == '5':
rotated += '2'
elif digit == '6':
rotated += '9'
elif digit == '8':
rotated += '8'
elif digit == '9':
rotated += '6'
else:
return False
return rotated != s
def count_good_numbers_brute_force(n):
count = 0
for i in range(1, n + 1):
if is_good(i):
count += 1
return count
An optimal solution involves dynamic programming. We can build the solution based on the length of the number. We can count the numbers of length i
which are good. A number is good if it contains at least one of the digits 2
, 5
, 6
, or 9
. These numbers are "rotating" numbers. Other digits such as 0, 1, and 8 are "self-rotating" numbers.
dp[i][0]
as the number of "good" numbers of length i
(containing at least one of 2, 5, 6, 9).dp[i][1]
as the number of all "valid" numbers of length i
(containing only 0, 1, 2, 5, 6, 8, 9).dp[0][0] = 0
, dp[1][0] = 4
(2, 5, 6, 9), dp[1][1] = 7
(0, 1, 2, 5, 6, 8, 9). Note that dp[0][1]
does not exist and is undefined. The reason is we cannot have a zero length number. However, for convenience, we can assume dp[0][1] = 1
.dp[i][1] = dp[i-1][1] * 7
(each digit can be any of 0, 1, 2, 5, 6, 8, 9).dp[i][0] = dp[i-1][0] * 7 + dp[i-1][1] * 4
(the "good" numbers can be formed by either adding a "safe" digit to a "good" number, or adding a "good" digit (2, 5, 6, 9) to a "safe" number).n
. Let s
be the string representation of n
. We can iterate through the digits of s
to calculate the count of good numbers.n
, (b) good numbers with length equal to the length of n
.def count_good_numbers(n):
s = str(n)
length = len(s)
dp = [[0, 0] for _ in range(length + 1)]
dp[0][1] = 1
for i in range(1, length + 1):
dp[i][1] = dp[i - 1][1] * 7
dp[i][0] = dp[i - 1][0] * 7 + dp[i - 1][1] * 4
ans = 0
safe = True
for i, digit in enumerate(s):
digit = int(digit)
if safe:
if i < length - 1:
if digit >= 0: # 0 is valid
ans += dp[length - 1 - i][0]
if digit >= 2:
ans += dp[length - 1 - i][1]
if digit >= 5:
ans += dp[length - 1 - i][1]
if digit >= 6:
ans += dp[length - 1 - i][1]
if digit >= 9:
ans += dp[length - 1 - i][1]
if digit in [0, 1, 8]:
pass
elif digit in [2, 5, 6, 9]:
ans += 0 # This is important
else:
safe = False
break
if i == length - 1 and safe:
if digit in [2, 5, 6, 9]:
ans += 1
return ans
n
, and the number of digits is log(n).dp
table stores log(n) elements.