2025-04-06:统计好整数的数目。用go语言,给定两个正整数 n 和 k,我们定义一个整数 x 为 k 回文整数的条件是:
1.x 是一个回文整数。
2.x 能被 k 整除。
如果一个整数的数位可以通过重新排列得到一个 k 回文整数,那么我们称这个整数为好整数。例如,对于 k = 2,整数 2020 可以重新排列为 2002,而 2002 是一个 k 回文整数,因此 2020 是一个好整数。但是,1010 不能重新排列成一个 k 回文整数,所以它不是好整数。
请计算在 n 个数位的整数中,有多少个是好整数。注意,任何整数在重新排列之前或之后都不能有前导零,例如 1010 在重排列后不能变成 101。
1 <= n <= 10。
1 <= k <= 9。
输入:n = 3, k = 5。
输出:27。
解释:
部分好整数如下:
551 ,因为它可以重排列得到 515 。
525 ,因为它已经是一个 k 回文整数。
题目来自leetcode3272。
解决步骤
-
生成所有n位回文数:
- 回文数的生成方法:对于n位数,如果n是偶数,则前一半数字和后一半数字对称;如果n是奇数,则前一半数字和后一半数字对称,中间数字独立。
- 例如,n=3(奇数),回文数的形式为
ABA
,其中A
是1-9的数字(避免前导零),B
是0-9的数字。 - 具体实现中,先生成前
(n+1)/2
位的数字,然后反转前n/2
位拼接到后面。
-
筛选能被k整除的回文数:
- 对于生成的每一个回文数,检查是否能被k整除。
- 如果能被k整除,则记录其数字的排列组合(即数字的多重集)。
-
统计数字多重集的唯一性:
- 对于每一个能被k整除的回文数,将其数字排序后存储到一个集合(
dict
)中,确保每个数字多重集只统计一次。 - 例如,回文数515和551的数字多重集是相同的(两个5和一个1),因此只记录一次。
- 对于每一个能被k整除的回文数,将其数字排序后存储到一个集合(
-
计算每个数字多重集对应的好整数数量:
- 对于每个唯一的数字多重集,计算其可以排列出的无前导零的n位数的数量。
- 计算方法:
- 首先计算不考虑前导零的所有排列数:
n! / (count[0]! * count[1]! * ... * count[9]!)
。 - 然后减去以0开头的排列数:
- 如果数字多重集中包含0,则以0开头的排列数为
(n-1)! / ((count[0]-1)! * count[1]! * ... * count[9]!)
。 - 总的好整数数量为:
总排列数 - 以0开头的排列数
。
- 如果数字多重集中包含0,则以0开头的排列数为
- 首先计算不考虑前导零的所有排列数:
- 代码中通过预计算阶乘来优化计算。
-
累加所有数字多重集的好整数数量:
- 将所有数字多重集对应的好整数数量累加,得到最终结果。
示例分析(n=3, k=5)
-
生成所有3位回文数:
- 形式为
ABA
,A
为1-9,B
为0-9。 - 共生成9(A的选择) * 10(B的选择) = 90个回文数。
- 形式为
-
筛选能被5整除的回文数:
- 能被5整除的回文数必须末位为0或5。
- 由于回文数的末位是
A
(第一位),所以A
必须是0或5。 - 但
A
不能为0(避免前导零),所以A
只能是5。 - 因此,回文数的形式为
5B5
,B
为0-9。 - 共10个回文数:505, 515, 525, ..., 595。
-
记录数字多重集:
- 对于
5B5
,数字多重集为两个5和一个B
。 B
从0到9,因此数字多重集为:- {5,5,0}, {5,5,1}, ..., {5,5,9}。
- 但
{5,5,0}
对应的排列不能以0开头,因此需要特殊处理。
- 对于
-
计算每个数字多重集的好整数数量:
- 以
{5,5,1}
为例:- 总排列数:
3! / (2! * 1!) = 3
(551, 515, 155)。 - 无前导零的排列数:3(均无前导零)。
- 总排列数:
- 以
{5,5,0}
为例:- 总排列数:
3! / (2! * 1!) = 3
(550, 505, 055)。 - 无前导零的排列数:3 - 1(055无效)= 2(550, 505)。
- 总排列数:
- 其他
{5,5,B}
(B≠0)的排列数均为3。 - 因此,总好整数数量为:
{5,5,0}
:2。{5,5,B}
(B=1..9):9 * 3 = 27。- 但实际
{5,5,5}
的排列数为1(555),所以需要修正:{5,5,5}
:3! / 3! = 1
。
- 因此:
{5,5,0}
:2。{5,5,1..4,6..9}
:8 * 3 = 24。{5,5,5}
:1。- 总计:2 + 24 + 1 = 27。
- 以
-
输出结果:
- 与题目描述一致,输出27。
时间复杂度和空间复杂度
-
时间复杂度:
- 生成回文数:
O(10^{(n+1)/2})
(即base
的取值范围)。 - 对于每个回文数:
- 检查是否能被k整除:
O(1)
。 - 记录数字多重集:
O(n log n)
(排序字符串)。
- 检查是否能被k整除:
- 计算排列数:
O(1)
(预计算阶乘)。 - 总时间复杂度:
O(10^{(n+1)/2} * n log n)
。 - 对于n=3,
10^2 * 3 log 3 ≈ 200
。
- 生成回文数:
-
空间复杂度:
- 存储数字多重集:
O(10^{(n+1)/2})
(最坏情况下每个回文数的数字多重集唯一)。 - 预计算阶乘:
O(n)
。 - 总空间复杂度:
O(10^{(n+1)/2} + n)
。 - 对于n=3,约为
100
。
- 存储数字多重集:
Go完整代码如下:
package main
import (
"fmt"
"sort"
"strconv"
"strings"
)
func countGoodIntegers(n int, k int) int64 {
dict := make(map[string]bool)
base := intPow(10, (n-1)/2)
skip := n & 1
/* 枚举 n 个数位的回文数 */
for i := base; i < base*10; i++ {
s := strconv.Itoa(i)
rev := reverseString(s)
s += rev[skip:]
palindromicInteger, _ := strconv.ParseInt(s, 10, 64)
/* 如果当前回文数是 k 回文数 */
if palindromicInteger%int64(k) == 0 {
chars := strings.Split(s, "")
sort.Strings(chars)
dict[strings.Join(chars, "")] = true
}
}
factorial := make([]int64, n+1)
factorial[0] = 1
for i := 1; i <= n; i++ {
factorial[i] = factorial[i-1] * int64(i)
}
var ans int64 = 0
for s := range dict {
cnt := make([]int, 10)
for _, c := range s {
cnt[c-'0']++
}
/* 计算排列组合 */
tot := int64(n-cnt[0]) * factorial[n-1]
for _, x := range cnt {
tot /= factorial[x]
}
ans += tot
}
return ans
}
func intPow(a, b int) int {
result := 1
for i := 0; i < b; i++ {
result *= a
}
return result
}
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func main() {
n := 3
k := 5
result := countGoodIntegers(n, k)
fmt.Println(result)
}
Python完整代码如下:
# -*-coding:utf-8-*-
def count_good_integers(n, k):
def int_pow(a, b):
return a ** b
def reverse_string(s):
return s[::-1]
def factorial(num):
if num == 0:
return 1
result = 1
for i in range(1, num + 1):
result *= i
return result
dict_set = set()
base = int_pow(10, (n - 1) // 2)
skip = n & 1
# 枚举 n 个数位的回文数
for i in range(base, base * 10):
s = str(i)
rev = reverse_string(s)
s += rev[skip:]
palindromic_integer = int(s)
# 如果当前回文数是 k 回文数
if palindromic_integer % k == 0:
chars = sorted(s)
dict_set.add(''.join(chars))
factorials = [factorial(i) for i in range(n + 1)]
ans = 0
for s in dict_set:
cnt = [0] * 10
for c in s:
cnt[int(c)] += 1
# 计算排列组合
tot = (n - cnt[0]) * factorials[n - 1]
for x in cnt:
tot //= factorials[x]
ans += tot
return ans
if __name__ == "__main__":
n = 3
k = 5
result = count_good_integers(n, k)
print(result)