小C的好数 | 豆包MarsCode AI刷题

88 阅读3分钟

1. 明确需求和约束

  • 输入:一个正整数 ( n )。
  • 输出:从 1 到 ( n ) 之间的“好数”的数量。
  • 限制条件
    • 数字不能包含前导零,但由于输入是正整数且范围从 1 到 ( n ),这一点天然满足。
    • 数字的数位中最多包含两种不同的数字。
  • 关键点:如何有效判断一个数字的数位是否符合“好数”的定义。

2. 分解问题

要解决此问题,可以将问题分解为以下几个步骤:

  1. 遍历从 1 到 ( n ) 的每个数字。
  2. 对每个数字,提取其数位,并判断该数字是否是“好数”:
    • 将数字转换为字符串。
    • 遍历字符串中的每一位,并记录这些数位。
    • 使用集合(Set)存储数位的独特值。
  3. 如果集合中不超过 2 个不同的数位,则这个数字是“好数”,将计数器加 1。
  4. 最后输出计数结果。

3. 算法设计

通过上述分析,可以设计出暴力枚举的方法。具体步骤如下:

  1. 初始化计数器:定义一个变量 count 来记录“好数”的数量。
  2. 遍历每个数字:从 1 到 ( n ) 的所有数字逐个检查。
  3. 判断是否是“好数”
    • 将数字转换为字符串,提取其数位。
    • 利用集合(Set)自动去重的特性,统计该数字中不同数位的数量。
    • 判断集合的大小是否小于等于 2。
  4. 计数:如果集合大小符合条件,则将计数器加 1。
  5. 输出结果:最后返回计数器的值。

4. 代码实现

以下是基于上述设计的 Java 代码实现:

import java.util.HashSet;
import java.util.Set;

public class Main {
    // 方法:判断从1到n之间的好数数量
    public static int solution(int n) {
        int count = 0;  // 初始化计数器
        
        // 遍历从1到n的所有数字
        for (int i = 1; i <= n; i++) {
            // 将数字转换为字符串
            String numStr = Integer.toString(i);
            
            // 使用集合记录不同的数位
            Set<Character> digits = new HashSet<>();
            for (char c : numStr.toCharArray()) {
                digits.add(c);
            }
            
            // 判断集合的大小是否小于等于2
            if (digits.size() <= 2) {
                count++;  // 满足条件的数字计数器加1
            }
        }
        
        return count;  // 返回好数的总数量
    }

    // 主方法:测试示例
    public static void main(String[] args) {
        // 测试用例
        System.out.println(solution(110));  // 输出102
        System.out.println(solution(1000)); // 输出352
        System.out.println(solution(1));    // 输出1
    }
}

5. 算法复杂度分析

时间复杂度

  • 外层循环遍历从 1 到 ( n ),需要 ( O(n) ) 的时间。
  • 对于每个数字,将其转换为字符串,并遍历其数位,时间复杂度为 ( O(d) ),其中 ( d ) 是数字的位数。对于最大数字 ( n ),其位数为 ( \log_{10}(n) )。
  • 因此,整体时间复杂度为 ( O(n \cdot \log_{10}(n)) )。

空间复杂度

  • 主要是存储数位的集合 Set,其大小最多为 10(数字的范围是 0 到 9)。
  • 因此,空间复杂度为 ( O(1) )。

7. 优化思考

虽然上述暴力枚举方法直观且易于实现,但对于非常大的 ( n )(例如 ( 10^9 ))可能会超时。可以考虑以下优化方法:

1. 预计算和记忆化

  • 利用动态规划或递归,预先计算出某个范围内“好数”的数量,避免重复计算。
  • 通过存储中间结果,减少重复计算的开销。

2. 数位动态规划(Digit DP)

  • 使用数位 DP 技术,通过逐位构造数字并统计“好数”的数量。
  • 数位 DP 的主要思想是逐位分析数字,判断是否符合条件,同时利用状态压缩来减少计算。
  • 数位 DP 的复杂度与数字的位数和状态空间有关,适合处理大范围问题,例如 ( n ) 接近 ( 10^9 )。