LeetCode每日一题2025-04-12
3272. 统计好整数的数目 H
给你两个 正 整数 n 和 k 。
如果一个整数 x 满足以下条件,那么它被称为 k 回文 整数 。
x是一个 回文整数 。x能被k整除。
如果一个整数的数位重新排列后能得到一个 k 回文整数 ,那么我们称这个整数为 好 整数。比方说,k = 2 ,那么 2020 可以重新排列得到 2002 ,2002 是一个 k 回文串,所以 2020 是一个好整数。而 1010 无法重新排列数位得到一个 k 回文整数。
请你返回 n 个数位的整数中,有多少个 好 整数。
注意 ,任何整数在重新排列数位之前或者之后 都不能 有前导 0 。比方说 1010 不能重排列得到 101 。
示例 1:
输入: n = 3, k = 5
输出: 27
解释:
部分好整数如下:
- 551 ,因为它可以重排列得到 515 。
- 525 ,因为它已经是一个 k 回文整数。
示例 2:
输入: n = 1, k = 4
输出: 2
解释:
两个好整数分别是 4 和 8 。
示例 3:
输入: n = 5, k = 6
输出: 2468
提示:
1 <= n <= 101 <= k <= 9
分析思路
我们可以把问题拆分为两个部分:
- 判断数位集合是否有回文排列
一个数的数位可以重新排列成回文数当且仅当:- 当 n 为偶数:所有数字出现次数均为偶数。
- 当 n 为奇数:最多只有一个数字出现次数为奇数,其余为偶数。
- 存在回文排列且该排列满足被 k 整除
- 由于我们要求构造的回文排列不能有前导 0,所以构造时需要保证最左位数字不为 0。
- 对于一个给定的符合“回文排列”条件的数位集合,若存在一种排列构成的回文数能被 k 整除,则我们将此多重集认为是合格的。
- 注意:一个 n 位整数只要其数位集合满足条件(存在至少一种排列构成合格的 k 回文数),那么所有拥有相同数位多重集的 n 位整数都是好整数。
构造方案
由于 n 的范围最多为 10,因此直接枚举所有 n 位回文数来检查被 k 整除的条件是可行的。根据 n 的奇偶性,我们可以分为两种情况:
-
n 为偶数:
设 m = n/2,回文数为:1
half + reverse(half)
枚举长度为 m 的数字串,其中第一个字符(对应整体最高位)不为 0。
-
n 为奇数:
设 m = n//2,则回文数为:1
half + mid + reverse(half)
其中 half 枚举同上(第一个字符非 0),mid 为 0~9 均可。
对于每个构造出来的回文数,如果它满足能被 k 整除的条件,则提取其“数字多重集”(即各个数字出现的次数),并存入一个集合中,确保对于同一多重集只记录一次。
计数方法
对于一个记录下来的数字多重集(长度为 n),所有的排列数为
但由于存在前导 0 的问题,对于包含 0 的情况,我们需要剔除那些首位为 0 的排列。
- 若多重集中 0 出现次数为 c0 > 0,则固定首位为 0后,剩余排列数为
- 故该多重集对应的合法 n 位数字数为
或者当 c0=0,则 valid = total。
对所有满足条件的多重集,将其合法排列数累加即为答案。
算法时间复杂度分析
-
枚举回文数
- 对于 n 为偶数:需要枚举 个数,最多约;
- 对于 n 为奇数:乘上 10 种中间数字情况,依然是 。
由于 n ≤ 10,最坏情形枚举数量不会超过 ,计算量是可接受的。
-
多重集计数
对于每个多重集,计算排列数的时间复杂度为 O(10)(数字个数恒定),因此总体时间复杂度主要取决于回文数枚举部分,为 。
总结
- 枚举所有符合 n 位、无前导 0 的回文数;
- 检查该回文数能否被 k 整除;
- 对满足条件的回文数,提取其数字多重集,并保证每个多重集只计入一次;
- 对每个多重集,计算所有排列中首位不为 0 的排列数,并累加结果。
代码实现:
1 | from math import factorial |
注意:需要对 n==1 的情况进行单独处理。由于一位数的重排列只能为其本身,所以对于一位数,只需要统计 1 到 9 中能被 k 整除的数字即可。
-
对 n==1 的处理
直接遍历 1~9 的单个数字,检查每个数字是否能被 k 整除,若能则计数。 -
回文数生成
-
当 n 为偶数时,构造回文数格式为
half + reverse(half)。 -
当 n 为奇数时,遍历中间数字
mid,构造回文数格式为half + mid + reverse(half)。注:此处保证
half的首位非 0。
- 多重集记录与排列数计算
- 将符合条件的回文数的数字多重集(各个数字出现的次数)存入集合避免重复计入。
- 利用阶乘及组合公式计算总排列数,再剔除那些首位为 0 的排列(当数字 0 存在时)。
