问题描述:
我们有一组排序的数字 D
,它是 {'1','2','3','4','5','6','7','8','9'}
的非空子集。(请注意,'0'
不包括在内。)
现在,我们用这些数字进行组合写数字,想用多少次就用多少次。例如D = {'1','3','5'}
,我们可以写出像'13', '551', '1351315'
这样的数字。
返回可以用D
中的数字写出的小于或等于N
的正整数的数目。
示例 :
输入:D = [“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.
提示:
D
是按排序顺序的数字'1'-'9'
的子集。1 <= N <= 10^9
问题分析:
(1)首先明确一个问题,就是给的数字集合 D
是不重复的,感觉这一点还是很重要的。
(2)假设数字 N
为 K
位, 很显然,K-1
的数字一定比它小,那么能组成 K-1
位的数有多少那?答案为(K-1)^(len(D))
,理解就是每一位有 len(D)
种选择,所以是它的次方个。
(3)由(2)可以求出位数为1 - (K-1) 位
的数的个数了,现在位数为 K
,其比 N
小的数字怎么求?动态规划方法
。
(4)–求位数为K且小于 N 的数字个数–, 思路:
(5)从数据集D
中依次拿出 一个数去和N
的最高位比较,如果比它小,后面各个位上的数字可以任意组合,通过求次方就可以得到。
(6)如果比它小,很显然,后面再怎么组合也是不符合要求的,所以直接舍弃。
(7)如果相等,现在也很显然,竟然最高位相等,那么就可以直接等于次高位
符合要求的个数(形成子问题 – 动态规划的一个必要条件)。然后把所有的累加,就是位数为 K
且小于N
的数字个数。
dp状态方程如下:
设:dp[i:] 表示,从左向开始,第 i 个数字表示最高位时的,符合要求的数字个数。
for i in range(K-1, -1, -1):
for d in D:
if d < S[i]:
dp[i] += len(D) ** (K-i-1)
elif d == S[i]:
dp[i] += dp[i+1]
Python3实现:
class Solution:
def atMostNGivenDigitSet(self, D, N):
S = str(N)
K = len(S)
dp = [0] * K + [1] # 初始化dp
for i in range(K-1, -1, -1): # 开机计算dp[i]
for d in D:
if d < S[i]: # 小于的情况
dp[i] += len(D) ** (K-i-1)
elif d == S[i]: # 相等的情况
dp[i] += dp[i+1]
return dp[0] + sum(len(D) ** i for i in range(1, K)) # 最终结果
if __name__ == '__main__':
D, N = ["1", "3", "5", "7"], 100
solu = Solution()
print(solu.atMostNGivenDigitSet(D, N))
声明: 总结学习,有问题可以批评指正,大神可以略过哦。
参考链接:leetcode.com/problems/numbers-at-most-n-given-digit-set/solution/