题目
如果整数 x 满足:对于每个数位 d ,这个数位 恰好 在 x 中出现 d 次。那么整数 x 就是一个 数值平衡数 。
给你一个整数 n ,请你返回 严格大于 n 的 最小数值平衡数 。
示例 1:
输入:n = 1 输出:22 解释: 22 是一个数值平衡数,因为:
- 数字 2 出现 2 次 这也是严格大于 1 的最小数值平衡数。
示例 2:
输入:n = 1000 输出:1333 解释: 1333 是一个数值平衡数,因为:
- 数字 1 出现 1 次。
- 数字 3 出现 3 次。 这也是严格大于 1000 的最小数值平衡数。 注意,1022 不能作为本输入的答案,因为数字 0 的出现次数超过了 0 。
示例 3:
输入:n = 3000 输出:3133 解释: 3133 是一个数值平衡数,因为:
- 数字 1 出现 1 次。
- 数字 3 出现 3 次。 这也是严格大于 3000 的最小数值平衡数。
提示:
0 <= n <= 10^6
解题思路
这道题的解题思路是:从 n+1 开始,逐个检查每个整数,直到找到第一个“数值平衡数”为止。
这个思路之所以可行,是因为题目给定的 n 的范围($n≤10^6$)并不大,而且“数值平衡数”在数轴上的分布虽然稀疏,但两个连续的平衡数之间的“空隙”也不会大到无法接受。
- 分析“数值平衡数”的特性
根据定义:对于每个在 $x$ 中出现的数位 $d$,$d$ 必须恰好出现 $d$ 次。
- 关键特性:数值平衡数中不能包含数字 0。
- 为什么?如果数字 0 出现了,根据定义,它必须出现 0 次。这本身就是一个矛盾。
- 因此,我们只需要考虑由 {1, 2, 3, 4, 5, 6, 7, 8, 9} 组成的数字。
- 例子:
22:数字2出现了2次。1333:数字1出现了1次,数字3P 出现了3次。122:数字1出现了1次,数字2出现了2次。
- 分析数据范围($n≤10^6$)
我们需要的答案是严格大于 $n$ 的最小平衡数。我们来看看 $n$ 在最大值 $10^6$ 附近的情况。
- $n=1,000,000$。
- 我们需要找到一个大于 $10^6$(即一个七位数)的最小平衡数。
- 一个平衡数的“长度”(即位数)等于它所包含的数位之和。例如
1333的长度是 1+3=4。 - 我们要找一个长度为 7 的最小平衡数。我们需要找到一组数字 ${d_1, d_2, ...}$,使得它们的和为 7。
- {7} -> 组成的数字是
7777777。 - {1, 6} -> 组成的数字是 {1, 6, 6, 6, 6, 6, 6}。最小的排列是
1666666。 - {2, 5} -> 组成的数字是 {2, 2, 5, 5, 5, 5, 5}。最小的排列是
2255555。 - {3, 4} -> 组成的数字是 {3, 3, 3, 4, 4, 4, 4}。最小的排列是
3334444。 - {1, 2, 4} -> 组成的数字是 {1, 2, 2, 4, 4, 4, 4}。最小的排列是
1224444。
- {7} -> 组成的数字是
- 比较上面这些7位数,最小的是
1224444。 - 这意味着,当 $n=10^6$ 时,我们要找的答案是
1224444。 - 在最坏的情况下(例如 $n=1,000,001$),我们也只需要从
1,000,002迭代检查到1,224,444,这个计算量(约22万次检查)是非常小的,完全可以在限制时间内完成。
- 算法设计
基于以上的分析,我们可以采用简单的“迭代搜索”策略。
主函数:nextBalancedNumber(n)
- 从
x = n + 1开始循环。 - 在循环中,调用一个辅助函数
isBalanced(x)来检查当前的x是否是数值平衡数。 - 如果
isBalanced(x)返回true,那么x就是我们要找的答案,直接返回x。 - 如果返回
false,则x++,继续下一次循环。
辅助函数:isBalanced(num)
这个函数用于判断一个数 num 是否是数值平衡数。
- 创建一个大小为 10 的数组(或哈希表)
counts,用于统计 0-9 每个数字出现的次数。 - 遍历
num的每一位(可以通过循环取模% 10和整除/ 10来实现)。 - 在遍历时,用
counts数组记录每个数位 d 出现的次数counts[d]。 - 遍历完成后,检查
counts数组(从索引 i=0 到 9):- 检查 0: 如果
counts[0] > 0,说明数字 0 出现了,这违反了平衡数的特性,直接返回false。 - 检查 1-9: 如果
counts[i] > 0(表示数位 i 至少出现了一次),并且counts[i] != i(表示它出现的次数不等于 i),则不满足平衡数定义,返回false。
- 检查 0: 如果
- 如果
counts数组的所有检查都通过了,说明num是一个数值平衡数,返回true。
具体代码
func nextBeautifulNumber(n int) int {
for x := n + 1; ; x++ {
if isBalanced(x) {
return x
}
}
}
// 数值平衡数判定:出现过的数字 d,其出现次数必须恰好等于 d;且不能含有 0。
func isBalanced(x int) bool {
cnt := [10]int{}
y := x
for y > 0 {
d := y % 10
cnt[d]++
y /= 10
}
// 不能含 0(因为 0 出现次数必须是 0)
if cnt[0] > 0 {
return false
}
// 对于出现过的数字 d(1..9),出现次数必须等于 d
for d := 1; d <= 9; d++ {
if cnt[d] > 0 && cnt[d] != d {
return false
}
}
return true
}