0788. 旋转数字
大约 4 分钟
0788. 旋转数字
- 标签:数学、动态规划
- 难度:中等
题目链接
题目大意
描述:给定搞一个正整数 。
要求:计算从 到 中有多少个数 是好数。
说明:
- 好数:如果一个数 的每位数字逐个被旋转 180 度之后,我们仍可以得到一个有效的,且和 不同的数,则成该数为好数。
- 如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。、 和 被旋转后仍然是它们自己; 和 可以互相旋转成对方(在这种情况下,它们以不同的方向旋转,换句话说, 和 互为镜像); 和 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。
- 的取值范围是 。
示例:
- 示例 1:
输入: 10
输出: 4
解释:
在 [1, 10] 中有四个好数: 2, 5, 6, 9。
注意 1 和 10 不是好数, 因为他们在旋转之后不变。
解题思路
思路 1:枚举算法
根据题目描述,一个数满足:数中没有出现 、、,并且至少出现一次 、、 或 ,就是好数。
因此,我们可以枚举 中的每一个正整数 ,并判断该正整数 的数位中是否满足没有出现 、、,并且至少一次出现了 、、 或 ,如果满足,则该正整数 位好数,否则不是好数。
最后统计好数的方案个数并将其返回即可。
思路 1:代码
class Solution:
def rotatedDigits(self, n: int) -> int:
check = [0, 0, 1, -1, -1, 1, 1, -1, 0, 1]
ans = 0
for i in range(1, n + 1):
flag = False
num = i
while num:
digit = num % 10
num //= 10
if check[digit] == 1:
flag = True
elif check[digit] == -1:
flag = False
break
if flag:
ans += 1
return ans
思路 1:复杂度分析
- 时间复杂度:。
- 空间复杂度:。
思路 2:动态规划 + 数位 DP
将 转换为字符串 ,定义递归函数 def dfs(pos, hasDiff, isLimit):
表示构造第 位及之后所有数位的合法方案数。其中:
- 表示当前枚举的数位位置。
- 表示当前是否用到 、、 或 中任何一个数字。
- 表示前一位数位是否等于上界,用于限制本次搜索的数位范围。
接下来按照如下步骤进行递归。
- 从
dfs(0, False, True)
开始递归。dfs(0, False, True)
表示:- 从位置 开始构造。
- 初始没有用到 、、 或 中任何一个数字。
- 开始时受到数字 对应最高位数位的约束。
- 如果遇到 ,表示到达数位末尾,此时:
- 如果 ,说明当前方案符合要求,则返回方案数 。
- 如果 ,说明当前方案不符合要求,则返回方案数 。
- 如果 ,则定义方案数 ,令其等于 ,即:
ans = 0
。 - 因为不需要考虑前导 ,所以当前所能选择的最小数字 为 。
- 根据 来决定填当前位数位所能选择的最大数字()。
- 然后根据 来枚举能够填入的数字 。
- 如果当前数位与之前数位没有出现 、、,则方案数累加上当前位选择 之后的方案数,即:
ans += dfs(pos + 1, hasDiff or check[d], isLimit and d == maxX)
。hasDiff or check[d]
表示当前是否用到 、、 或 中任何一个数字或者没有用到 、、。isLimit and d == maxX
表示 位受到之前位限制和 位限制。
- 最后的方案数为
dfs(0, False, True)
,将其返回即可。
思路 2:代码
class Solution:
def rotatedDigits(self, n: int) -> int:
check = [0, 0, 1, -1, -1, 1, 1, -1, 0, 1]
# 将 n 转换为字符串 s
s = str(n)
@cache
# pos: 第 pos 个数位
# hasDiff: 之前选过的数字是否包含 2,5,6,9 中至少一个。
# isLimit: 表示是否受到选择限制。如果为真,则第 pos 位填入数字最多为 s[pos];如果为假,则最大可为 9。
def dfs(pos, hasDiff, isLimit):
if pos == len(s):
# isNum 为 True,则表示当前方案符合要求
return int(hasDiff)
ans = 0
# 不需要考虑前导 0,则最小可选择数字为 0
minX = 0
# 如果受到选择限制,则最大可选择数字为 s[pos],否则最大可选择数字为 9。
maxX = int(s[pos]) if isLimit else 9
# 枚举可选择的数字
for d in range(minX, maxX + 1):
# d 不在选择的数字集合中,即之前没有选择过 d
if check[d] != -1:
ans += dfs(pos + 1, hasDiff or check[d], isLimit and d == maxX)
return ans
return dfs(0, False, True)
思路 2:复杂度分析
- 时间复杂度:。
- 空间复杂度:。