【LeetCode 902】最大为 N 的数字组合 - 详细题解与数位组合思路

【LeetCode 902】最大为 N 的数字组合 - 详细题解与数位组合思路

题目描述

给定一个按升序排列的数字数组 digits 和一个整数 n,返回用 digits 中的数字组合出的、小于或等于 n 的正整数的个数。


解题思路(数位 DP / 组合计数)

我们可以将统计满足条件的数字个数分为 两大类 进行讨论:

1. 位数小于 n 的数字

如果构造的数字位数比 n 的位数(设为 lenlenlen)小,那么无论怎么选,数值一定小于 n

  • 长度为 1 的数字个数:m1m^1m1
  • 长度为 2 的数字个数:m2m^2m2
  • ...
  • 长度为 len−1len-1len−1 的数字个数:mlen−1m^{len-1}mlen−1

我们可以直接通过循环累加 m1+m2+⋯+mlen−1m^1 + m^2 + \dots + m^{len-1}m1+m2+⋯+mlen−1。

2. 位数等于 n 的数字

n 转化为字符串 sss,逐位(从高位到低位 iii)进行比较:

  1. 当前位选择小于 sisisi 的数字
    • 此时后续剩余的 len−1−ilen - 1 - ilen−1−i 个位置可以任意选择 digits 中的数字。
    • 方案增加数:m(len−1−i)m^{(len - 1 - i)}m(len−1−i)。
  2. 当前位选择等于 sisisi 的数字
    • 标记当前位可以匹配(same = true),并继续考察下一位 i+1i+1i+1。
  3. 当前位没有可选的数字小于或等于 sisisi
    • 如果 digits 中没有与 sisisi 相同的数字,说明无法继续向后构造与 n 相同的数,此时直接返回累加结果。

如果成功匹配完所有的 lenlenlen 位,说明可以构造出一个完全等于 n 的数字,最终答案额外 +1+1+1。


C++ 代码实现

cpp 复制代码
class Solution {
public:
    int atMostNGivenDigitSet(vector<string>& digits, int n) {
        string s = to_string(n);
        int m = digits.size();
        int len = s.size();
        long long ans = 0;
        
        // 1. 统计位数小于 len 的所有组合数
        long long tem = 1;
        for (int i = 1; i < len; i++) {
            tem *= m;
            ans += tem;
        }

        // 2. 统计位数等于 len 且小于等于 n 的组合数
        for (int i = 0; i < len; i++) {
            bool same = false;
            for (auto &d : digits) {
                if (d[0] < s[i]) {
                    // 当前位选小于 s[i] 的数,后续位可任意组合
                    long long tem2 = 1;
                    for (int j = i + 1; j < len; j++) {
                        tem2 *= m;
                    }
                    ans += tem2;
                } else if (d[0] == s[i]) {
                    // 找到相等的数字,标记可以继续比对下一位
                    same = true;
                }
            }
            // 如果 digits 中没有与 s[i] 相等的数字,无法继续向前匹配,直接返回
            if (!same) {
                return ans;
            }
        }

        // 遍历完成说明可以构造出完全等于 n 的数字
        return ans + 1;
    }
};

复杂度分析

  • 时间复杂度 :O(len×m)\mathcal{O}(len \times m)O(len×m),其中 lenlenlen 为数字 nnn 的十进制位数(不超过 10),mmm 为 digits 数组的大小。循环层数和数字位数成正比,运行耗时极低(可达 0 ms)。
  • 空间复杂度 :O(len)\mathcal{O}(len)O(len),仅用于将整数 nnn 转换为字符串存储。
相关推荐
辰烨chenye2 小时前
LeetCode Hot 100 题解 · 哈希篇
算法·leetcode·哈希算法
罗西的思考2 小时前
DreamZero 与 DreamDojo:世界模型与策略的分层协同综合分析与对比
人工智能·算法·机器学习
玖玥拾3 小时前
LeetCode 219 存在重复元素 II
算法·leetcode·哈希算法·散列表
知无不研4 小时前
c语言中循环的介绍与简单应用
c语言·开发语言·算法·循环·for·while
a187927218315 小时前
【算法】动态规划第四篇:背包收官——min 哨兵、计数世界与组合排列分水岭
算法·leetcode·动态规划·dp·01背包·算法讲解·决策合并
2601_962297485 小时前
在python3中、下列输出变量a的正确写法是_2020超星大数据Python免费答案
数据结构·python·算法·编程·字符串操作
辰烨chenye5 小时前
LeetCode Hot 100 题解 · 子串篇
算法·leetcode·职场和发展
辰烨chenye6 小时前
LeetCode Hot 100 题解 · 堆篇
java·算法·leetcode