|
| 1 | +''' |
| 2 | +A confusing number is a number that when rotated 180 degrees becomes a different number with each digit valid. |
| 3 | +
|
| 4 | +We can rotate digits of a number by 180 degrees to form new digits. |
| 5 | +
|
| 6 | +When 0, 1, 6, 8, and 9 are rotated 180 degrees, they become 0, 1, 9, 8, and 6 respectively. |
| 7 | +When 2, 3, 4, 5, and 7 are rotated 180 degrees, they become invalid. |
| 8 | +Note that after rotating a number, we can ignore leading zeros. |
| 9 | +
|
| 10 | +For example, after rotating 8000, we have 0008 which is considered as just 8. |
| 11 | +Given an integer n, return the number of confusing numbers in the inclusive range [1, n] |
| 12 | +''' |
| 13 | + |
| 14 | + |
| 15 | +class Solution: |
| 16 | + def confusingNumberII(self, N: int) -> int: |
| 17 | + rotatables = [1, 0, 6, 8, 9] |
| 18 | + rotation_map = {1:1, 0:0, 6:9, 8:8, 9:6} |
| 19 | + confusing_number_count = 0 |
| 20 | + |
| 21 | + def count_rotatables(cur_num=0): |
| 22 | + nonlocal confusing_number_count |
| 23 | + if cur_num * 10 > N: |
| 24 | + return |
| 25 | + for num in rotatables: |
| 26 | + next_num = cur_num*10 + num |
| 27 | + if next_num == 0: |
| 28 | + continue |
| 29 | + confusing_number_count += next_num <= N and is_valid_rotation(next_num) |
| 30 | + count_rotatables(next_num) |
| 31 | + |
| 32 | + def is_valid_rotation(num): |
| 33 | + rotated = 0 |
| 34 | + cur_num = num |
| 35 | + while cur_num: |
| 36 | + rotated = rotated * 10 + rotation_map[cur_num%10] |
| 37 | + cur_num //= 10 |
| 38 | + return rotated != num |
| 39 | + |
| 40 | + count_rotatables() |
| 41 | + |
| 42 | + return confusing_number_count |
| 43 | + |
| 44 | +------------------------------------------------------------------------------------------ |
| 45 | +class Solution: |
| 46 | + def confusingNumberII(self, N: int) -> int: |
| 47 | + rotations = {1:1, 6:9, 8:8, 9:6, 0:0} |
| 48 | + valid = [0, 1, 6, 8, 9] |
| 49 | + |
| 50 | + def backtrack(num, rotation, position): |
| 51 | + if num > N: |
| 52 | + return |
| 53 | + |
| 54 | + if num != rotation: |
| 55 | + count[0] += 1 |
| 56 | + |
| 57 | + for digit in valid: |
| 58 | + backtrack(num * 10 + digit, rotations[digit] * position + rotation, position * 10) |
| 59 | + |
| 60 | + count = [0] |
| 61 | + for digit in valid[1:]: |
| 62 | + backtrack(digit, rotations[digit], 10) |
| 63 | + return count[0] |
| 64 | + |
| 65 | +------------------------------------------------------------------------ |
| 66 | +Main idea: Call numbers that are the same as themselves after rotating 180 degrees "cyclic numbers". Call all numbers that are still valid after rotating 180 degrees "valid numbers". The number of confusing numbers is the number of valid numbers minus the number of cyclic numbers. |
| 67 | + |
| 68 | +There are lots of edge cases in this problem so the code is not very clean. |
| 69 | + |
| 70 | +class Solution(object): |
| 71 | + def confusingNumberII(self, n): |
| 72 | + """ |
| 73 | + :type n: int |
| 74 | + :rtype: int |
| 75 | + """ |
| 76 | + N = max(len(str(n)), 3) |
| 77 | + g = [-1] * N # g(n) = num of exact n digit cyclic numbers |
| 78 | + f = [-1] * N # n digit cyclic where two ends can be 0's |
| 79 | + g[0] = 1 |
| 80 | + g[1] = 2 |
| 81 | + f[0] = 1 |
| 82 | + f[1] = 3 |
| 83 | + for i in range(2,N): |
| 84 | + g[i] = 4 * f[i-2] |
| 85 | + f[i] = sum(g[j] for j in range(i, -1, -2)) |
| 86 | + |
| 87 | + if i%2 != 0: |
| 88 | + # we missed all zeros situation for odd i's |
| 89 | + f[i] += 1 |
| 90 | + |
| 91 | + cds = [0, 1, 6, 8, 9] # confusing digits |
| 92 | + A = list(str(n)) |
| 93 | + A = [int(a) for a in A] |
| 94 | + |
| 95 | + # count the number of valid numbers <= n |
| 96 | + valid_count = 0 |
| 97 | + for i in range(len(A)): |
| 98 | + smaller_num = len([j for j in cds if j<A[i]]) |
| 99 | + valid_count += smaller_num * 5**(len(A)-i-1) # definetly within range |
| 100 | + |
| 101 | + if A[i] not in cds: |
| 102 | + break |
| 103 | + else: |
| 104 | + if i==len(A)-1: |
| 105 | + valid_count+=1 |
| 106 | + print("valid_count", valid_count) |
| 107 | + |
| 108 | + flip = {0:0,1:1,6:9,8:8,9:6} |
| 109 | + |
| 110 | + # count the number of cyclic numbers |
| 111 | + ptr = 0 |
| 112 | + cyclic_count = 0 |
| 113 | + lastptr = len(A) - 1 |
| 114 | + backdigits = [] # list of set back digits, start from the last reverse order |
| 115 | + while ptr < lastptr: |
| 116 | + smaller_num = len([i for i in cds if i<A[ptr]]) # |
| 117 | + if smaller_num >= 1: # if 0 < A[ptr] |
| 118 | + if len(backdigits) > 0: |
| 119 | + cyclic_count += smaller_num * f[(lastptr-ptr-1)] |
| 120 | + else: |
| 121 | + cyclic_count += (smaller_num-1) * f[(lastptr-ptr-1)] # if not 0 |
| 122 | + cyclic_count += sum(g[0:(lastptr-ptr)+1]) # if we choose 0 |
| 123 | + |
| 124 | + if A[ptr] in cds: |
| 125 | + backdigits.insert(0, flip[A[ptr]]) |
| 126 | + ptr += 1 |
| 127 | + lastptr -= 1 |
| 128 | + else: |
| 129 | + break |
| 130 | + |
| 131 | + print("cyclic", cyclic_count) |
| 132 | + |
| 133 | + # check if we reached the middle |
| 134 | + if ptr > lastptr: |
| 135 | + # strart from the ptr, if everything fixed so far is fine, add 1 |
| 136 | + adder = 1 |
| 137 | + j = 0 |
| 138 | + for i in range(ptr, len(A)): |
| 139 | + if A[i] > backdigits[j]: |
| 140 | + adder = 1 |
| 141 | + break |
| 142 | + elif A[i] < backdigits[j]: |
| 143 | + adder = 0 |
| 144 | + break |
| 145 | + j += 1 |
| 146 | + cyclic_count += adder |
| 147 | + elif ptr == lastptr: |
| 148 | + # strart from the ptr, if everything fixed so far is fine, add 1 |
| 149 | + single_cyclic_set = [0,1,8] |
| 150 | + cyclic_count += len([i for i in single_cyclic_set if i<A[ptr]]) |
| 151 | + # count ways for the center digit. These will be smaller than A |
| 152 | + if A[ptr] in single_cyclic_set: |
| 153 | + adder = 1 |
| 154 | + j = 0 |
| 155 | + for i in range(ptr+1, len(A)): |
| 156 | + if A[i] < backdigits[j]: |
| 157 | + adder = 0 |
| 158 | + break |
| 159 | + elif A[i] > backdigits[j]: |
| 160 | + adder = 1 |
| 161 | + break |
| 162 | + j += 1 |
| 163 | + cyclic_count += adder |
| 164 | + |
| 165 | + result = valid_count - cyclic_count |
| 166 | + return result |
0 commit comments