目录

0902:最大为 N 的数字组合(1989 分)

力扣第 101 场周赛第 3 题

题目

给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[i] 来写的数字。例如,如果 digits = ['1','3','5'],我们可以写数字,如 '13', '551', 和 '1351315'

返回 可以生成的小于或等于给定整数 n 的正整数的个数

示例 1:

输入:digits = ["1","3","5","7"], n = 100
输出:20
解释:
可写出的 20 个数字是:
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.

示例 2:

输入:digits = ["1","4","9"], n = 1000000000
输出:29523
解释:
我们可以写 3 个一位数字,9 个两位数字,27 个三位数字,
81 个四位数字,243 个五位数字,729 个六位数字,
2187 个七位数字,6561 个八位数字和 19683 个九位数字。
总共,可以使用D中的数字写出 29523 个整数。

示例 3:

输入:digits = ["7"], n = 8
输出:1

提示:

  • 1 <= digits.length <= 9
  • digits[i].length == 1
  • digits[i] 是从 '1''9' 的数
  • digits 中的所有值都 不同
  • digits非递减顺序 排列
  • 1 <= n <= 109

分析

#1

  • 典型的数位 dp,令 dfs(i, st, bd) 代表某个状态下的结果:
    • 遍历到 n 的第 i 位
    • st 代表前面是否取了数
    • bd 代表前面取的数是否贴着 n 的上界
  • 递归即可
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int:
        @cache
        def dfs(i,st,bd):
            if i==len(s):
                return st
            res = 0
            up = s[i] if bd else '9'
            for x in digits:
                if x>up:
                    break
                res += dfs(i+1,1,bd and x==up)
            if not st:
                res += dfs(i+1,0,0)
            return res

        s = str(n)
        return dfs(0,0,1)

0 ms

#2

  • 也可以利用排列组合知识分段计算
  • 例如对于 n=7382,digits=[“1”,“3”,“5”,“7”]:
    • [0, 1000) 的对应个数:4+4^2+4^3
    • [1000, 7000) 的对应个数:3*4^3
    • [7000, 7300) 的对应个数:1*4^2
    • [7300, 7380) 的对应个数:4*4
    • [7380, 7382) 的对应个数:0
  • 具体实现时,设 s=str(n) 的长度为 m
    • 先计算 0 到 10^(m-1) 的对应个数
    • 遍历 i,固定 s[:i] 不变,求能取的比 s[i] 小的值,再乘以后面的排列个数
    • 一旦 s[i] 不在 digits 中,就已经统计完毕了
  • 为了方便,可以先将 n 加 1,变为开区间

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int:
        s = str(n+1)
        m,k = len(s),len(digits)
        res = (pow(k,m)-k)//(k-1) if k>1 else k*(m-1)
        for i,c in enumerate(s):
            w = sum(x<c for x in digits)
            res += w*pow(k,m-1-i)
            if c not in digits:
                break
        return res

0 ms