问题描述
小C对“好数”非常感兴趣,她定义一个不含前导零的正整数为“好数”,如果它的所有数位最多包含两种不同的数字。例如,数字 23,2323,9,111,和 101 都是好数。现在小C想知道,从1到 nnn 之间有多少个好数。
例如:当 n=110n = 110n=110 时,所有的1位数、2位数,以及一些3位数(如 100, 101)都是好数,一共有 102 个。
测试样例
-
样例 1:
- 输入:n=110n = 110n=110
- 输出:102
-
样例 2:
- 输入:n=1000n = 1000n=1000
- 输出:352
-
样例 3:
- 输入:n=1n = 1n=1
- 输出:1
思路分析
-
定义“好数” :
- 一个“好数”是一个不含前导零的正整数,且其所有数位最多包含两种不同的数字。举例来说,数字 9、101、23 都是“好数”,而数字 123、234 等则不是,因为它们含有超过两种不同的数字。
-
问题转化:
- 问题的核心是计算从 1 到 nnn 之间有多少个数字是“好数”。
- 我们可以遍历所有从 1 到 nnn 的数字,然后对于每个数字检查它是否符合“好数”的定义。如果符合条件,则计数器增加。
-
判断数字是否为“好数” :
- 将数字转化为字符串,遍历每个数字字符,使用集合
HashSet来存储这些字符。由于HashSet会自动去重,因此我们只需要判断HashSet中不同元素的数量。如果元素数量不超过 2,则该数字为“好数”。
- 将数字转化为字符串,遍历每个数字字符,使用集合
-
算法步骤:
- 遍历从 1 到 nnn 的每个数字。
- 对于每个数字,检查其是否是“好数”。
- 如果是“好数”,计数器增加 1。
- 最后返回计数器的值,即从 1 到 nnn 之间的“好数”数量。
Java代码实现
import java.util.HashSet;
public class Main {
// 主方法:计算从 1 到 n 之间的好数数量
public static int solution(int n) {
int count = 0; // 初始化计数器,用来记录好数的数量
// 遍历从 1 到 n 的所有数字
for (int i = 1; i <= n; i++) {
if (isGoodNumber(i)) { // 如果该数字是好数,计数器加 1
count++;
}
}
return count; // 返回好数的总数
}
// 辅助方法:判断一个数字是否是好数
private static boolean isGoodNumber(int number) {
String numStr = String.valueOf(number); // 将数字转为字符串
HashSet<Character> digits = new HashSet<>(); // 用 HashSet 存储数字的不同字符
// 遍历数字字符串中的每个字符
for (char c : numStr.toCharArray()) {
digits.add(c); // 将字符添加到 HashSet 中
if (digits.size() > 2) { // 如果不同的数字种类超过 2,返回 false
return false;
}
}
return true; // 如果数字中不超过 2 种不同的数字,返回 true
}
public static void main(String[] args) {
// 进行一些基本的测试,检查结果是否正确
System.out.println(solution(110) == 102); // 输出 true,表示 solution(110) 的结果是正确的
System.out.println(solution(1000) == 352); // 输出 true,表示 solution(1000) 的结果是正确的
System.out.println(solution(1) == 1); // 输出 true,表示 solution(1) 的结果是正确的
}
}
代码分析
-
solution(int n)方法:- 该方法是问题的核心部分,负责计算从 1 到 nnn 之间的好数数量。
- 它遍历从 1 到 nnn 的每个数字,并调用
isGoodNumber(i)来检查该数字是否为好数。如果是好数,增加计数。 - 最后返回计数器
count,即符合条件的好数数量。
-
isGoodNumber(int number)方法:- 这个方法检查给定的数字是否为好数。通过将数字转换为字符串,然后遍历字符串的每个字符,将字符存入
HashSet中。 - 如果
HashSet中的元素个数超过 2,则说明该数字包含超过两种不同的数字,返回false。否则,返回true,说明该数字是一个好数。
- 这个方法检查给定的数字是否为好数。通过将数字转换为字符串,然后遍历字符串的每个字符,将字符存入
-
main(String[] args)方法:-
在
main方法中,我们进行了几组测试:solution(110)应该输出 102。solution(1000)应该输出 352。solution(1)应该输出 1。
-
每个测试结果与预期结果进行比对,验证代码的正确性。
-
时间复杂度分析
- 外层循环:我们遍历从 1 到 nnn 的每个数字,时间复杂度为 O(n)O(n)O(n)。
- 内层循环:对于每个数字,我们将其转换为字符串并遍历每个字符,时间复杂度是 O(d)O(d)O(d),其中 ddd 是数字的位数。通常情况下,数字的位数 ddd 是对数级别的,即 d=log10(n)d = \log_{10}(n)d=log10(n)。
- 因此,整个程序的时间复杂度是 O(n⋅d)O(n \cdot d)O(n⋅d),其中 ddd 通常是 O(log10(n))O(\log_{10}(n))O(log10(n)),所以总体复杂度接近 O(n)O(n)O(n)。
总结
- 该代码通过简单的遍历和判断每个数字是否为“好数”来解决问题。对于每个数字,我们通过
HashSet来统计其中不同的数字种类,若种类不超过 2,则认为它是好数。 - 该算法的时间复杂度为 O(n⋅log(n))O(n \cdot \log(n))O(n⋅log(n)),在 nnn 较小的情况下表现良好,适用于中等规模的输入。