力扣题解(统计特殊整数)

2376. 统计特殊整数

已解答

困难

相关标签

相关企业

提示

如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。

给你一个  整数 n ,请你返回区间 [1, n] 之间特殊整数的数目。

思路:

若n的位数是k,则对于位数在1-k-1的所有数,都满足一定小于n,则可以直接全求出来,基本方法就是对于位数是h的所有数,最高位可以从1-9中取得,有9种可能,次高位可以取0-9中除了最高位取得的那一位之外的数,同样有9种可能,再下一位也是从0-9种除了最高和次高两位之外的数,有8种可能,以此类推下去,就是有7,6,5....种可能,因此可以直接全求出来,式子是9*9*8*...(有几位能乘几个数)。

其次,对于位数是k位的,同样分两种情况,最高位小于n和最高位等于n。对于最高位小于n的数,除了第一位是固定之外,其余位数可以从0-9中任意取得。而对于最高位等于n的数又可以对次高位分成次高位小于和次高位等于的情况,判断方式类似,因此可以知道此处用了递归的策略。

此外,对于求有k位的数,假设前两位取12,和21,对于后面几位数的取法其实是一致的,因此可以用记忆搜索发,将前缀所用到的数字保存起来,若后面遇到相同的前缀,则可以直接取得结果。

此处保存前缀是利用一个数mask,将其看成二进制数是,第i位为1则表示第i位已经用过了,比如前缀是12,则二进制是110,也就是数字6。而对于保存前缀和,此处对于是高位和n相等和高位小于n,会影响到后面几位的结果,因此这两种情况需要分开,因此可以采用mask向左移动一位,然后用最低位的0,1表示是否是高位和n相等的情况。

class Solution {
public:
    unordered_map<int, int> memo;
    int countSpecialNumbers(int n) {
        string nStr = to_string(n);
        int res = 0;
        int prod = 9;
        for (int i = 0; i < nStr.size() - 1; i++) {
            res += prod;
            prod *= 9 - i;
        }
        res += dp(0, false, nStr);
        return res;
    }

      int dp(int mask, bool judge, const string& st)
    {
        if (__builtin_popcount(mask) == st.size())
            return 1; //此时只有可能是st本身

        int key = mask * 2 + (judge ? 1 : 0);//使得每个数字唯一对应一个下标
        if (memo.count(key))
            return memo[key]; //记忆搜索

        int ret = 0;
        int low = mask == 0 ? 1 : 0; //低位,mask为0的时候从1开始,用于处理最高位不能为0
        int high = judge? 9 : st[__builtin_popcount(mask)] - '0';
  
        for (int i = low; i <= high; i++)
        {
            if (((mask >> i) & 1) == 0)
                ret += dp(mask | (1 << i), judge || i < high, st);
        }
        memo[key] = ret;
        return memo[key];

    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值