力扣 2376. 统计特殊整数

题目来源:https://leetcode.cn/problems/count-special-integers/

大致题意:
给一个整数 n,求出 1~n 所有的特殊整数。特殊整数是指整数中每一位的数字只出现一次。

思路

本来想尝试打表解决,结果内存越界了

考虑到递归的深度和广度,正确的思路应该是使用 dfs 解题,这里给出 dfs + 记忆化搜索的解法

dfs + 记忆化搜索

将给定的整数 n 转为字符数组,设其长度为 len,从高位到低位搜索每一位可能出现的数字

假设当前遍历的位置为 i,使用整数 mask 表示当前使用过的数字(若数字 x 出现过,那么 mask 二进制的第 x 位为 1),使用标记位 isLimited 表示当前数位的最大值是否受限(受限是指当前数位的数字给的足够大时,会导致本轮搜索的数字大于数字 n,比如数字 n 为 5678,当前枚举到百位,而千位给的数字是 5,那么当前数位的最大值即受限,最大为 6,而不是 9),使用标记为 isNum 表示在当前位之前是否已经选择了数字(即从高位搜索到低位的过程中,高位是可以跳过不选择数字的,但是一旦有高位选择了数字,则当前位一定要选择数字)

搜索时,使用一个二维的数组记录 dfs 搜索过的结果,dp 第一维为搜索的位数,第二维为当前的 mask,仅记录当前搜索数位不受限的情况,这样防止出错(比如若 n 为 8657,之前搜索时选择高位为 86 和 68 的 mask 是相同的,但是实际上 68 的数位选择不受限,接下来两位的搜索结果更多)

那么每次搜索到当前位,若当前位已经到达字符数组末尾,表示已经搜索完成,根据当前的标记位 isNum 返回 1 或者 0;否则,判断当前位是否已经搜索过,若搜索过直接返回结果;最后,则根据标记位枚举当前位可以出现的数字范围,并根据 mask 判断对应数字是否出现过,若未出现过,则统计结果

具体看代码:

public class CountSpecialNumbers {
    int[][] dp;
    char[] nums;
    int len;
    public int countSpecialNumbers(int n) {
        nums = String.valueOf(n).toCharArray();
        len = nums.length;
        dp = new int[len][1 << 10];
        for (int i = 0; i < len; i++) {
            Arrays.fill(dp[i], -1);
        }
        return dfs(0, 0, true, false);
    }

    public int dfs(int i, int mask, boolean isLimited, boolean isNum) {
        // 若遍历到最后一位,根据当前是否是数字返回结果
        if (i == len) {
            // 若是数字,表示当前为一种特殊整数,返回 1,否则返回 0
            return isNum ? 1 : 0;
        }
        // 若当前数位不受限,且当前遍历位之前已经遍历过,可以直接返回之前的结果
        if (!isLimited && dp[i][mask] != -1) {
            return dp[i][mask];
        }
        // 获取当前位的可能的开始位置和结束位置
        int start = isNum ? 0 : 1;
        int end = isLimited ? nums[i] - '0' : 9;
        // 当前位能有的选择个数
        int sum = 0;
        // 若当前为不是数字,可以选择跳过
        if (!isNum) {
            // 只要跳过一个数字,那么就不收数位限制了
            sum = dfs(i + 1, mask, false, false);
        }
        // 遍历统计所有结果
        for (int j = start; j <= end; j++) {
            if (((mask >> j) & 1) == 0) {
                // isLimited && j == end 即只有当前数位受限且当前位选择最大数位时下一位才受限
                // 当前位已经选数,所以下一位一定为数字,isNum 为 ture
                sum += dfs(i + 1, mask | (1 << j), isLimited && j == end, true);
            }
        }
        // 记忆搜索结果
        if (!isLimited) {
            dp[i][mask] = sum;
        }
        return sum;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

三更鬼

谢谢老板!

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

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

打赏作者

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

抵扣说明:

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

余额充值