902. 最大为 N 的数字组合

这篇博客讨论了一个关于如何利用给定非递减数字数组digits生成小于或等于特定整数n的正整数数量的问题。博主提出了一种数学思路,首先计算一位数和二位数的情况,然后通过遍历n的每一位并与digits数组对比,动态调整计数,尤其关注数组中有与n的某位数字相同的特殊情况。博客中还提供了具体的代码实现,展示了如何处理这种问题并避免重复计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

给定一个按 非递减顺序 排列的数字数组 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

思路:多看几遍问题,其实就是用升序数组中的1个或多个数字,能拼成多少比n更小的数。

可以用数学的思路来解决本问题:
比如 digits = [“1”,“3”,“7”,“9”], n = 800

1. 比n小的数字,n是三位数,则二位数和一位数怎么拼凑都是肯定小于n的,二位数的拼凑可能有 4x2 = 8, 一位数是 4x1 = 4,共122. 只需要考虑三位数,n的第一位是8, 则只有 1 3 7为首位的三位数小于n,9开头的首位必大于n,所以三位数有 3x4x4=48 
(首位从数组中可以取三个数 1 3 7,第二位可以取四个数,个位也可以取四个数)
4. 所以总共是 12+48 = 60

上述推理后大致就明白了思路,那如果数组中有数字和首位相同呢?
比如 digits = [“1”,“3”,“8”,“9”], n = 800

1. 和上面相同,二位数和一位数都是122. 三位数中,当首位为1 3时,比较第一位“8”,后面两位任取肯定小于n,3. 所以有2x4x4=324. 当首位为8时,开始比较第二位 “0”,发现第二位全部大于0,这种情况肯定无需比较第三位了,所以8为首位且小于n的数字并没有。
5. 同理,如果第二位全部小于n的第二位,则任选即可,这种情况也无需比较第三位,因为第三位选择哪个数都小于。
6. 所以重点还是数组中有和第二位相同元素时,需要特殊考虑,去比较下一位。后面仍然如此类推。

综上,我决定用删减去处理三位数的选择,就是先默认数组中所有元素皆可选择,4x4x4=64.
然后再开始遍历数组与n的各位数字进行比较,若大于,则直接减去 4x4x1=16,如果遍历到了相同元素,就中断遍历,开始比较第二位,同样操作,一直把所有大于n的可能数字剔除掉。

public int atMostNGivenDigitSet(String[] digits, int n) {

        int count = 0;

        char[] nc = String.valueOf(n).toCharArray();

        // 小于n的位数数字任取
        for (int i = 1; i < nc.length; i++) {
            count += Math.pow(digits.length,i);
        }


        /**
         * 集中处理n位的数字
         * 从n位后面一位开始,逐个进行比较
         * i 数字n的第几位数字
         * j 每次都从数组最大的数开始和n的对应位数比较
         */

        // 先默认所有的都可以,再开始删减
        count += Math.pow(digits.length,nc.length);
        boolean flag = true;
        outA: for (int i = 0; i < nc.length; i++) {
            outB: for (int j = digits.length-1; j >= 0; j--) {
                // 每次比较时都默认true,如果某次遍历全部都比数组元素大,则结束
                flag = true;

                // 数字的每一位数data
                int data = (int) nc[i] - (int)'0';
                // 如果小于,直接把后面的count加上去
                if(Integer.parseInt(digits[j]) < data){
                    // 如果小于,直接结束
                    break outA;
                }else if(Integer.parseInt(digits[j]) == data){
                    // 如果是等于,则继续比较后面的数字,开始下一位数的比较
                    flag = false;
                    break outB;

                }else{
                    // 如果大于,直接开始删减
                    count -= (Math.pow(digits.length,nc.length-i-1));
                }
            }

            // 如果所有的数字都大于当前数组数字,那就不用再比较了
            if(flag){
                break;
            }
        }

        return count;

    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值