902. 最大为 N 的数字组合
题目描述
给一个非递顺序排列的数组数组,可以用任意次数组中的元素来写数字,
问能生生成的<=给定整数n的个数?
其中数组中的值都不同,数组长度在1-9之间。
基本分析
- 这是啥类型的题?之前没有遇到过,经典的数位dp的题
- 涉及到任意区间合法数查询时考虑怎么做,这个题有啥简化的地方?
- 可以假定存在函数dp[x],能返回区间[1,x]内的合法数个数,可以配合容斥原理,得到任意区间合法数的查询ans[l, r]=dp[r]-dp[l-1]
- 在这个题中,查询的左端点是固定的1,同时dp[x]=0,答案就是dp[x]
- 考虑求dp[x]的时候,组成[1, x]的合法数可以怎么进行分类?
- 位数和x相同,最高位<x最高位,记为res1
- 位数和x相同,最高位=x最高位,记为res2
- 位数< x,记为res3
- 以上分类的数据分别可以怎么进行处理?
- 假如数字是x,数字的长度是n,数组长度m,找到最高位<x最高位的可取的个数n_h,剩下的位数n-1可以看做是n-1个坑,每个坑有m中放法,那么个数就是,这里需要注意break
- 当首位相等的时候,需要看后面的位数。这里以第k位举例。假如x的第k为为cur,在nums中能找到的<=cur值的下标是r,同样需要分类讨论
- nums[r]=cur,这个时候k位置有r个选择,后面的位置有n-k-1个坑,每个坑m个选择
- nums[r]<cur,这个时候k位置有r+1个选择,后面的位置有n-k-1个坑。需要注意的是由于nums[r]<cur,往后的的方案数已经被这次统计完成了,累加后要break。
- nums[r]>cur, 这个分支不再能满足要求,合法数为0。
- 当位数<x的时候,分别计算1到n-1的可能,进行累加
代码
数位dp+二分
class Solution: def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int: x = n nums = [int(c) for c in digits] x_list = [int(c) for c in str(x)] # n表示整数x的长度,m表示可选数组长 n, m = len(x_list), len(nums) ans = 0 # 下一步需要考虑数字长度和n的关系,先遍历解决=n,再遍历解决<n # 数字长度=n情况 for i in range(n): cur = x_list[i] # nums中找<=cur的位置 l, r = 0, m-1 while l < r: mid = l + r + 1 >> 1 if nums[mid] <= cur: l = mid else: r = mid - 1 # 当前位>情况,直接退出 if nums[l] > cur: break # 当前位为=情况,累加 elif nums[l] == cur: # 当前位置为l个,后面有n-1-l个坑,每个坑m个选择 ans += l * math.pow(m, n-1-i) # 当前位置如果是最后一位,需要+1 if i == n-1: ans += 1 # 当前为<情况,累加 elif nums[l] < cur: #当前位置为l+1个 ans += (l+1) * math.pow(m, n-1-i) break # 处理长度<n的情况,从1位开始累加 # 这里的last实际求的是m**(i-1) last = 1 for i in range(1, n): cur = last * m ans += cur last = cur return int(ans)
复杂度
时间:因为nums数组长度最大是9,因此二分复杂度可以忽略,整体复杂度是n的长度,因此
空间:
总结
- 在代码实现上,通过从高到低遍历每一位,将res1和res2放到了一起解决。逻辑上是
- i=0时,拿到首位cur,nums中找满足<=cur的值
- 如果 >直接退出
- 如果< 算完直接退出
- 如果等于,算完这一位,去考虑第二位i=1的情况
- i=1时类似于上面的步骤,继续进行
- i=0时,拿到首位cur,nums中找满足<=cur的值
- 处理长度在1到n-1的数字时候,直接进行小到大的累加
- 在处理nums[r]=cur的时候,注意有个i==n-1的判断?因为在最后一位的时候,前面公式只是统计了<的情况;如果到了最后一位,=也是能满足要求的。
- 二分的时候,是找<=x的最大值,用的是二分方法2,定义mid=l+r+1>>1, 找到的l也可能不满足要求(最左边本来就比cur大)
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现