【leetcode】【2022/10/18】902. 最大为 N 的数字组合

问题描述:

  • 给定一个按非递减顺序排列的数字数组 digits
    • 你可以用任意次数 digits[i] 来写的数字,例如如果 digits = ['1','3','5'],我们可以写数字,如 13, 5511351315
  • 返回可以生成的小于或等于给定整数 n 的正整数的个数。

核心思路:

  • 该题考察了数位 dp,看了题解,学习了如何套用模板来写记忆化搜索。【具体看参考内容链接,用模板写两道类型题加深印象即可】
    • 模板中有很多值得注意的细节,可以结合文字和代码中注释加以理解。
  • 首先要理解要如何去做该题,所有满足条件正整数需要小于等于 n,因此可以反过来想,所有小于等于 n 的数字中,有多少个满足条件呢?【在该题中,所谓的满足条件就是数字中每一位数均存在于集合 digits 中】
    • 当然不需要将 [1, n] 中所有数字都进行遍历,我们可以将 n 的位数 m 作为构造的条件,也就是说,记忆化搜索只需要维护一个状态 idx 来记录当前的位数,idx 的取值范围为 [0, m),此时我们只需要一直记录用高 idx 位来构造数字的结果即可(注意是高 idx 位)。【记忆化搜索维护的状态其实就是动态规划的状态,该题只有一个状态,所以代码中的 dp 数组只有一维】
  • 而下面的代码模板中,dfs 函数还维护了两个变量 is_limitis_num,这两个变量很关键,简单来说,这两个变量是用于停止条件以及剪枝。
    • 介绍 is_limit 变量的作用:首先要理解,在当前位 idx 上,插入的数字可能是 0~9,但有一种情况下,当前位上的数字选择可能会使数字超过 n,那就是此前所有位上的选择都和 n 上对应位的选择是一样的;举例子来说,n = 123,如果前两位选择为 12,则当前第三位只能选择 0~3,但如果前两位选择为 11,则当前第三位能够选择 0~9;因此 is_limit 就是用来记录前面数位的选择是否受到了 n 的约束。【当 is_limit = true,则说明此前数位受到了 n 的约束】
    • 介绍 is_num 变量的作用:该变量其实就是用来确定此前的数位是否跳过了构造,因此可以在此前一直跳过,一旦开始构造,后面就不能再跳过;换句话说,当 n = 1234 时,我们想要构造一个数为 34 则需要跳过高两位。【当 is_num = false 则说明目前仍然未有开始构造数字】
  • 代码里面还有很多细节,其他细节可看参考内容链接。

代码实现:

  • 记忆化搜索解法代码实现如下:【模板来自参考内容】
    class Solution
    {
    public:
        int atMostNGivenDigitSet(vector<string>& digits, int n)
        {
            string s = to_string(n);
            int m = s.size();
            vector<int> dp(m, -1);
            function<int(int, bool, bool)> dfs = [&] (int idx, bool is_limit, bool is_num) -> int
            {
                if(idx == m) return is_num; // 来到尾后位置,需要用标志 is_num 来判断是否已经形成数字,如果为 true 则返回 1
                if(!is_limit and is_num and dp[idx] > 0) return dp[idx];
                int ans = 0;
                if(!is_num) // 如果仍然没有生成数字,就可以尝试跳过,构造更少位数的数字
                    ans += dfs(idx+1, false, false); // 此时必然没有受约束,所以两个标志都置为 false 即可
                char hi = is_limit ? s[idx] : '9';
                for(auto& lo : digits) // 注意 lo 为 string,而 hi 为 char
                {
                    if(lo[0] > hi) break;
                    ans += dfs(idx+1, is_limit and lo[0] == hi, true);
                }
                if(!is_limit and is_num) dp[idx] = ans;
                return ans;
            };
            return dfs(0, true, false);
        }
    };
    

参考内容:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值