2025-04-06:统计好整数的数目。用go语言,给定两个正整数 n 和 k,我们定义一个整数 x 为 k 回文整数的条件是: 1.x 是一个回文整数。 2.

22 阅读6分钟

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。

解决步骤

  1. 生成所有n位回文数

    • 回文数的生成方法:对于n位数,如果n是偶数,则前一半数字和后一半数字对称;如果n是奇数,则前一半数字和后一半数字对称,中间数字独立。
    • 例如,n=3(奇数),回文数的形式为ABA,其中A是1-9的数字(避免前导零),B是0-9的数字。
    • 具体实现中,先生成前(n+1)/2位的数字,然后反转前n/2位拼接到后面。
  2. 筛选能被k整除的回文数

    • 对于生成的每一个回文数,检查是否能被k整除。
    • 如果能被k整除,则记录其数字的排列组合(即数字的多重集)。
  3. 统计数字多重集的唯一性

    • 对于每一个能被k整除的回文数,将其数字排序后存储到一个集合(dict)中,确保每个数字多重集只统计一次。
    • 例如,回文数515和551的数字多重集是相同的(两个5和一个1),因此只记录一次。
  4. 计算每个数字多重集对应的好整数数量

    • 对于每个唯一的数字多重集,计算其可以排列出的无前导零的n位数的数量。
    • 计算方法:
      • 首先计算不考虑前导零的所有排列数:n! / (count[0]! * count[1]! * ... * count[9]!)
      • 然后减去以0开头的排列数:
        • 如果数字多重集中包含0,则以0开头的排列数为(n-1)! / ((count[0]-1)! * count[1]! * ... * count[9]!)
        • 总的好整数数量为:总排列数 - 以0开头的排列数
    • 代码中通过预计算阶乘来优化计算。
  5. 累加所有数字多重集的好整数数量

    • 将所有数字多重集对应的好整数数量累加,得到最终结果。

示例分析(n=3, k=5)

  1. 生成所有3位回文数

    • 形式为ABAA为1-9,B为0-9。
    • 共生成9(A的选择) * 10(B的选择) = 90个回文数。
  2. 筛选能被5整除的回文数

    • 能被5整除的回文数必须末位为0或5。
    • 由于回文数的末位是A(第一位),所以A必须是0或5。
    • A不能为0(避免前导零),所以A只能是5。
    • 因此,回文数的形式为5B5B为0-9。
    • 共10个回文数:505, 515, 525, ..., 595。
  3. 记录数字多重集

    • 对于5B5,数字多重集为两个5和一个B
    • B从0到9,因此数字多重集为:
      • {5,5,0}, {5,5,1}, ..., {5,5,9}。
    • {5,5,0}对应的排列不能以0开头,因此需要特殊处理。
  4. 计算每个数字多重集的好整数数量

    • {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。
  5. 输出结果

    • 与题目描述一致,输出27。

时间复杂度和空间复杂度

  1. 时间复杂度

    • 生成回文数:O(10^{(n+1)/2})(即base的取值范围)。
    • 对于每个回文数:
      • 检查是否能被k整除:O(1)
      • 记录数字多重集:O(n log n)(排序字符串)。
    • 计算排列数:O(1)(预计算阶乘)。
    • 总时间复杂度:O(10^{(n+1)/2} * n log n)
    • 对于n=3,10^2 * 3 log 3 ≈ 200
  2. 空间复杂度

    • 存储数字多重集: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)

在这里插入图片描述