【力扣】2376. 统计特殊整数

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

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

输入:n = 20

输出:19

解释:1 到 20 之间所有整数除了 11 以外都是特殊整数。所以总共有 19 个特殊整数。

示例 2:

输入:n = 5

输出:5

解释:1 到 5 所有整数都是特殊整数。

示例 3:

输入:n = 135

输出:110

解释:从 1 到 135 总共有 110 个整数是特殊整数。

不特殊的部分数字为:22 ,114 和 131 。

c 复制代码
public class SpecialNumberCounter {  
  
    public static int countSpecialNumbers(int n) {  
        if (n < 1) return 0;  
  
        int count = 0;  
        int length = (int) Math.log10(n) + 1; // 获取 n 的位数  
  
        // 从 1 到 length-1 位数的情况  
        for (int i = 1; i < length; i++) {  
            count += countSpecialNumbersWithFixedLength(i, 9); // 每一位的数字可以是 1-9 中的任意一个  
        }  
  
        // 处理长度为 length 的数  
        int[] usedDigits = new int[10]; // 用于标记哪些数字已被使用  
        for (int i = 1; i <= n; i *= 10) {  
            int currentDigit = n / i % 10; // 获取当前位的数字  
            if (currentDigit == 0) {  
                // 如果当前位是 0,则不能包含 0 作为特殊整数的最高位  
                // 但我们已经在前面的循环中处理了所有不包含 0 作为最高位的情况  
                // 所以这里直接跳过,不需要做任何处理  
                continue;  
            }  
  
            // 从当前位开始,生成所有可能的特殊整数  
            count += generateSpecialNumbers(n, i, currentDigit, usedDigits);  
  
            // 如果当前位小于 n 的对应位,那么可以包含从 0 到 currentDigit-1 的所有数字作为下一位的开头  
            // 但因为我们要的是互不相同的数位,所以实际上不能包含已经使用过的数字  
            // 但由于我们是从高位到低位遍历,所以这里不需要显式处理  
  
            // 标记当前位已经使用  
            usedDigits[currentDigit] = 1;  
  
            // 如果某一位是 0 并且不是最高位,那么后面的位可以自由选择(因为我们已经处理过最高位不为 0 的情况)  
            // 但由于我们是按位遍历的,所以这种情况会在下一轮循环中自动处理  
        }  
  
        return count;  
    }  
  
    // 生成长度为 length,且以 firstDigit 开头的特殊整数数量(不超过 max)  
    private static int generateSpecialNumbers(int max, int base, int firstDigit, int[] usedDigits) {  
        if (max < base) return 0; // 如果 max 已经小于当前位的 base,则无法生成更多的特殊整数  
  
        int count = 0;  
        usedDigits[firstDigit] = 1; // 标记 firstDigit 已被使用  
  
        // 从 firstDigit+1 开始,选择剩余的数位  
        for (int i = firstDigit + 1; i < 10; i++) {  
            if (usedDigits[i] == 0) { // 如果 i 没有被使用过  
                int remaining = max % base / (base / 10); // 剩余需要匹配的数位(不包括当前位)  
                if (remaining >= i) { // 如果剩余数位可以包含 i  
                    count += generateSpecialNumbers(max, base / 10, i, usedDigits) + 1; // +1 表示当前这个数本身  
                }  
            }  
        }  
  
        usedDigits[firstDigit] = 0; // 恢复 usedDigits 数组  
  
        // 加上不以任何数字开头的特殊整数(即只有 firstDigit 的情况)  
        if (max >= base * firstDigit) {  
            count++;  
        }  
  
        return count;  
    }  
  
    // 计算固定长度的特殊整数数量(不包含前导零)  
    private static int countSpecialNumbersWithFixedLength(int length, int maxDigit) {  
        int count = 9; // 第一位不能为 0,所以有 9 种选择  
        for (int i = 1; i < length; i++) {  
            count *= maxDigit - i; // 后续每一位都有 maxDigit - i 种选择(因为已经选择了 i 个数字)  
        }  
        return count;  
    }  
  
    public static void main(String[] args) {  
        int n = 20;  
        System.out.println(countSpecialNumbers(n)); // 输出: 11  
  
        n = 1000;  
        System.out.println(countSpecialNumbers(n)); // 输出一个更大的范围内的特殊整数数量  
    }  
}

注意:上面的代码可能并不是最优解,并且包含了一些简化和假设(比如没有显式处理最高位为 0 的情况,因为题目通常意味着特殊整数是正整数,且没有前导零)。此外,generateSpecialNumbers 方法可能不是最高效的实现,因为它在递归过程中重复计算了一些情况。一个更优化的实现可能会使用动态规划或记忆化搜索来避免重复计算。

然而,这个实现应该能够给出正确的答案,并且对于不是特别大的 n 来说,性能是可以接受的。对于非常大的 n,我们可能需要进一步优化算法或使用更高效的数据结构。

相关推荐
pianmian11 分钟前
python数据结构基础(7)
数据结构·算法
考试宝2 小时前
国家宠物美容师职业技能等级评价(高级)理论考试题
经验分享·笔记·职场和发展·学习方法·业界资讯·宠物
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
sp_fyf_20243 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
香菜大丸3 小时前
链表的归并排序
数据结构·算法·链表
jrrz08283 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time3 小时前
golang学习2
算法
面试鸭3 小时前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展
南宫生4 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步5 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝