普通视图

发现新文章,点击刷新页面。
今天 — 2025年4月12日LeetCode 每日一题题解

搜索 & 枚举

作者 tsreaper
2024年9月1日 00:08

解法:搜索 & 枚举

因为好整数是 $k$ 回文整数的排列,因此我们可以把数字频率相同的 $k$ 回文整数视为同一种。枚举每种 $k$ 回文整数,再算算它不以 $0$ 为开头的排列有几种即可。

怎样枚举每种 $k$ 回文整数呢?由于回文数要求前半和后半相同,因此我们可以通过 DFS 搜索回文数的前半部分填什么。找到一个 $k$ 回文整数之后,再统计它的数字频率即可。这一部分的复杂度是 $\mathcal{O}(b^{\frac{n}{2}} \times b)$,其中 $b = 10$ 是可选的数字种类。因为 $n$ 很小所以没问题。

最后一个问题:给定每种数字的频率,怎么求不以 $0$ 为开头的排列有几种?我们可以用全排列数扣掉以 $0$ 为开头的排列。带重复元素的全排列数公式为

$$\frac{n!}{\prod n_i!}$$

其中 $n_i$ 是第 $i$ 种元素的出现频率。那么设 $c_i$ 为数字 $i$ 出现的频率,答案就是

$$
\frac{n!}{\prod\limits_{i = 0}^9 c_i!} - \frac{(n - 1)!}{(c_0 - 1)! \times \prod\limits_{i = 1}^9 c_i!}
$$

因为对于每种 $k$ 回文整数,我们需要 $\mathcal{O}(b)$ 的复杂度计算答案,这一部分的复杂度也是 $\mathcal{O}(b^{\frac{n}{2}} \times b)$。

参考代码(c++)

###cpp

class Solution {
public:
    long long countGoodIntegers(int n, int K) {
        long long P[n];
        P[0] = 1;
        for (int i = 1; i < n; i++) P[i] = P[i - 1] * 10;

        int cnt[10] = {0};
        // 用 string 保存每种数的出现次数,因为 vector 不能放进 unordered_set 里
        unordered_set<string> st;
        auto dfs = [&](auto &&self, int l, int r, long long now) {
            if (l > r) {
                // 所有数都填完了,检查能否被 K 整除,并记录数字出现频率
                if (now % K != 0) return;
                string s;
                for (int i = 0; i < 10; i++) s.push_back(cnt[i]);
                st.insert(s);
                return;
            }

            // 枚举第 l 和 r 位放什么数,注意不能有前导 0
            for (int i = (r == n - 1 ? 1 : 0); i < 10; i++) {
                if (l == r) {
                    cnt[i]++;
                    self(self, l + 1, r - 1, now + i * P[l]);
                    cnt[i]--;
                } else {
                    cnt[i] += 2;
                    self(self, l + 1, r - 1, now + i * (P[l] + P[r]));
                    cnt[i] -= 2;
                }
            }
        };
        dfs(dfs, 0, n - 1, 0);

        // 预处理阶乘
        long long fac[n + 1];
        fac[0] = 1;
        for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i;

        long long ans = 0;
        // 枚举每种 K 回文整数
        for (auto &s : st) {
            // 全排列数
            long long a = fac[n];
            for (int i = 0; i < 10; i++) a /= fac[s[i]];
            ans += a;
            if (s[0] == 0) continue;
            // 减去以 0 为开头的排列数
            a = fac[n - 1];
            for (int i = 0; i < 10; i++) {
                int t = (i == 0 ? s[i] - 1 : s[i]);
                a /= fac[t];
            }
            ans -= a;
        }
        return ans;
    }
};

[Python3/Java/C++/Go/TypeScript] 一题一解:枚举+组合数学(清晰题解)

作者 lcbin
2025年4月12日 09:44

方法一:枚举 + 组合数学

我们可以考虑枚举所有长度为 $n$ 的回文数,判断它们是否是 $k$ 回文数。由于回文数的性质,我们只需要枚举前半部分的数字,然后将其反转拼接到后面即可。

前半部分的数字长度为 $\lfloor \frac{n - 1}{2} \rfloor$,那么前半部分的数字范围是 $[10^{\lfloor \frac{n - 1}{2} \rfloor}, 10^{\lfloor \frac{n - 1}{2} \rfloor + 1})$。我们可以将前半部分的数字拼接到后面,形成一个长度为 $n$ 的回文数。注意到,如果 $n$ 是奇数,则需要将中间的数字做特殊处理。

然后,我们判断该回文数是否是 $k$ 回文数,如果是,则统计该回文数的所有排列组合。为了避免重复,我们可以使用一个集合 $\textit{vis}$ 来存储已经出现过的回文数的每个最小排列。如果该回文数的最小排列已经出现过,则跳过该回文数。否则,我们统计该回文数有多少个不重复的排列组合,添加到答案中。

我们可以使用一个数组 $\textit{cnt}$ 来统计每个数字出现的次数,然后使用组合数学的公式计算排列组合的数量。具体来说,假设数字 $0$ 出现了 $x_0$ 次,数字 $1$ 出现了 $x_1$ 次,...,数字 $9$ 出现了 $x_9$ 次,那么该回文数的排列组合数量为:

$$
\frac{(n - x_0) \cdot (n - 1)!}{x_0! \cdot x_1! \cdots x_9!}
$$

其中 $(n - x_0)$ 表示最高位可以选择除 $0$ 以外的所有数字,而 $(n - 1)!$ 表示除最高位以外的所有数字的排列组合数量,然后我们除以每个数字出现的次数的阶乘,避免重复。

最后,我们将所有的排列组合数量相加,得到最终的答案。

###python

class Solution:
    def countGoodIntegers(self, n: int, k: int) -> int:
        fac = [factorial(i) for i in range(n + 1)]
        ans = 0
        vis = set()
        base = 10 ** ((n - 1) // 2)
        for i in range(base, base * 10):
            s = str(i)
            s += s[::-1][n % 2 :]
            if int(s) % k:
                continue
            t = "".join(sorted(s))
            if t in vis:
                continue
            vis.add(t)
            cnt = Counter(t)
            res = (n - cnt["0"]) * fac[n - 1]
            for x in cnt.values():
                res //= fac[x]
            ans += res
        return ans

###java

class Solution {
    public long countGoodIntegers(int n, int k) {
        long[] fac = new long[n + 1];
        fac[0] = 1;
        for (int i = 1; i <= n; i++) {
            fac[i] = fac[i - 1] * i;
        }

        long ans = 0;
        Set<String> vis = new HashSet<>();
        int base = (int) Math.pow(10, (n - 1) / 2);

        for (int i = base; i < base * 10; i++) {
            String s = String.valueOf(i);
            StringBuilder sb = new StringBuilder(s).reverse();
            s += sb.substring(n % 2);
            if (Long.parseLong(s) % k != 0) {
                continue;
            }

            char[] arr = s.toCharArray();
            Arrays.sort(arr);
            String t = new String(arr);
            if (vis.contains(t)) {
                continue;
            }
            vis.add(t);
            int[] cnt = new int[10];
            for (char c : arr) {
                cnt[c - '0']++;
            }

            long res = (n - cnt[0]) * fac[n - 1];
            for (int x : cnt) {
                res /= fac[x];
            }
            ans += res;
        }

        return ans;
    }
}

###cpp

class Solution {
public:
    long long countGoodIntegers(int n, int k) {
        vector<long long> fac(n + 1, 1);
        for (int i = 1; i <= n; ++i) {
            fac[i] = fac[i - 1] * i;
        }

        long long ans = 0;
        unordered_set<string> vis;
        int base = pow(10, (n - 1) / 2);

        for (int i = base; i < base * 10; ++i) {
            string s = to_string(i);
            string rev = s;
            reverse(rev.begin(), rev.end());
            s += rev.substr(n % 2);
            if (stoll(s) % k) {
                continue;
            }
            string t = s;
            sort(t.begin(), t.end());
            if (vis.count(t)) {
                continue;
            }
            vis.insert(t);
            vector<int> cnt(10);
            for (char c : t) {
                cnt[c - '0']++;
            }
            long long res = (n - cnt[0]) * fac[n - 1];
            for (int x : cnt) {
                res /= fac[x];
            }
            ans += res;
        }
        return ans;
    }
};

###go

func factorial(n int) []int64 {
fac := make([]int64, n+1)
fac[0] = 1
for i := 1; i <= n; i++ {
fac[i] = fac[i-1] * int64(i)
}
return fac
}

func countGoodIntegers(n int, k int) (ans int64) {
fac := factorial(n)
vis := make(map[string]bool)
base := int(math.Pow(10, float64((n-1)/2)))

for i := base; i < base*10; i++ {
s := strconv.Itoa(i)
rev := reverseString(s)
s += rev[n%2:]
num, _ := strconv.ParseInt(s, 10, 64)
if num%int64(k) != 0 {
continue
}
bs := []byte(s)
slices.Sort(bs)
t := string(bs)

if vis[t] {
continue
}
vis[t] = true
cnt := make([]int, 10)
for _, c := range t {
cnt[c-'0']++
}
res := (int64(n) - int64(cnt[0])) * fac[n-1]
for _, x := range cnt {
res /= fac[x]
}
ans += res
}

return
}

func reverseString(s string) string {
t := []byte(s)
for i, j := 0, len(t)-1; i < j; i, j = i+1, j-1 {
t[i], t[j] = t[j], t[i]
}
return string(t)
}

###ts

function countGoodIntegers(n: number, k: number): number {
    const fac = factorial(n);
    let ans = 0;
    const vis = new Set<string>();
    const base = Math.pow(10, Math.floor((n - 1) / 2));

    for (let i = base; i < base * 10; i++) {
        let s = `${i}`;
        const rev = reverseString(s);
        if (n % 2 === 1) {
            s += rev.substring(1);
        } else {
            s += rev;
        }

        if (+s % k !== 0) {
            continue;
        }

        const bs = Array.from(s).sort();
        const t = bs.join('');

        if (vis.has(t)) {
            continue;
        }

        vis.add(t);

        const cnt = Array(10).fill(0);
        for (const c of t) {
            cnt[+c]++;
        }

        let res = (n - cnt[0]) * fac[n - 1];
        for (const x of cnt) {
            res /= fac[x];
        }
        ans += res;
    }

    return ans;
}

function factorial(n: number): number[] {
    const fac = Array(n + 1).fill(1);
    for (let i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i;
    }
    return fac;
}

function reverseString(s: string): string {
    return s.split('').reverse().join('');
}

###rust

impl Solution {
    pub fn count_good_integers(n: i32, k: i32) -> i64 {
        use std::collections::HashSet;
        let n = n as usize;
        let k = k as i64;
        let mut fac = vec![1_i64; n + 1];
        for i in 1..=n {
            fac[i] = fac[i - 1] * i as i64;
        }

        let mut ans = 0;
        let mut vis = HashSet::new();
        let base = 10_i64.pow(((n - 1) / 2) as u32);

        for i in base..base * 10 {
            let s = i.to_string();
            let rev: String = s.chars().rev().collect();
            let full_s = if n % 2 == 0 {
                format!("{}{}", s, rev)
            } else {
                format!("{}{}", s, &rev[1..])
            };

            let num: i64 = full_s.parse().unwrap();
            if num % k != 0 {
                continue;
            }

            let mut arr: Vec<char> = full_s.chars().collect();
            arr.sort_unstable();
            let t: String = arr.iter().collect();
            if vis.contains(&t) {
                continue;
            }
            vis.insert(t);

            let mut cnt = vec![0; 10];
            for c in arr {
                cnt[c as usize - '0' as usize] += 1;
            }

            let mut res = (n - cnt[0]) as i64 * fac[n - 1];
            for &x in &cnt {
                if x > 0 {
                    res /= fac[x];
                }
            }
            ans += res;
        }

        ans
    }
}

###js

/**
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var countGoodIntegers = function (n, k) {
    const fac = factorial(n);
    let ans = 0;
    const vis = new Set();
    const base = Math.pow(10, Math.floor((n - 1) / 2));

    for (let i = base; i < base * 10; i++) {
        let s = String(i);
        const rev = reverseString(s);
        if (n % 2 === 1) {
            s += rev.substring(1);
        } else {
            s += rev;
        }

        if (parseInt(s, 10) % k !== 0) {
            continue;
        }

        const bs = Array.from(s).sort();
        const t = bs.join('');

        if (vis.has(t)) {
            continue;
        }

        vis.add(t);

        const cnt = Array(10).fill(0);
        for (const c of t) {
            cnt[parseInt(c, 10)]++;
        }

        let res = (n - cnt[0]) * fac[n - 1];
        for (const x of cnt) {
            res /= fac[x];
        }
        ans += res;
    }

    return ans;
};

function factorial(n) {
    const fac = Array(n + 1).fill(1);
    for (let i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i;
    }
    return fac;
}

function reverseString(s) {
    return s.split('').reverse().join('');
}

时间复杂度 $({10}^m \times n \times \log n)$,空间复杂度 $O({10}^m \times n)$,其中 $m = \lfloor \frac{n - 1}{2} \rfloor$。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈😄~

每日一题-统计好整数的数目🔴

2025年4月12日 00:00

给你两个  整数 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 <= 10
  • 1 <= k <= 9

回溯+全排列模板题

作者 sierpinski-f
2024年9月1日 09:42

Problem: 3272. 统计好整数的数目

[TOC]

思路

先回溯找到所有可能的回文串,然后统计每个回文串的排列个数,注意排列不能0开头

Code

###Python3

@cache
def fac(x):
    if x == 0:
        return 1
    return x * fac(x-1)

class Solution:
    def countGoodIntegers(self, n: int, k: int) -> int:
        pow10 = [1] * n
        for i in range(1, n):
            pow10[i] = pow10[i - 1] * 10 % k

        st = set()
        m = (n + 1) // 2
        res = []
        def dfs(i: int, j: int) -> None:
            if i == m:
                if j == 0:
                    cur = res.copy()
                    cur += cur[:n // 2]
                    cur.sort()
                    st.add("".join(cur))

                return
            
            mn = 1 if i == 0 else 0
            for d in range(mn, 10):  
                if n % 2 and i == m - 1:  # 正中间
                    j2 = (j + d * pow10[i]) % k
                else:
                    j2 = (j + d * (pow10[i] + pow10[-1 - i])) % k
                res.append(str(d))
                dfs(i + 1, j2)
                res.pop()
            
        dfs(0, 0)
        ans = 0
        for x in st:
            cnt = Counter(x)
            f = fac(n)
            for v in cnt.values():
                f //= fac(v)
            
            # 减去0开头的
            if cnt['0'] > 0:
                f1 = fac(n-1) 
                for k, v in cnt.items():
                    if k == '0':
                        f1 //= fac(v - 1)
                    else:
                        f1 //= fac(v)
                f -= f1 
            ans += f 
        
        return ans

枚举所有回文数+组合数学(Python/Java/C++/Go)

作者 endlesscheng
2024年9月1日 08:48

考虑枚举所有长为 $n$ 的回文数。

首先,知道了回文数的左半边,就知道了回文数的右半边,所以可以枚举回文数的左半边。

设 $m = \left\lfloor\dfrac{n-1}{2}\right\rfloor$,设 $\textit{base} = 10^m$。

在 $[\textit{base}, 10\cdot\textit{base})$ 范围内枚举所有长为 $n$ 的回文数的左半边。

如果回文数 $x$ 能被 $k$ 整除,那么接下来需要解决的问题有两个:

  1. 计算 $x$ 有多少个不同的排列。
  2. 不能重复统计。

为了保证不重复统计,可以像 49. 字母异位词分组 那样,把 $x$ 的十进制字符串 $s$ 排序,如果之前遇到过同样的字符串 $t$,那么 $s$ 生成的所有排列,$t$ 也能生成。用哈希表记录排序后的字符串,如果 $s$ 排序后在哈希表中,那么就跳过。

下面是组合数学时间。

本质上计算的是「有重复元素的排列个数」。

统计 $s$ 中的每个数字的出现次数 $\textit{cnt}$。

先填最高位。由于不能有前导零,最高位可以填的数有 $n-\textit{cnt}_0$ 个。其余 $n-1$ 个数随便排,有 $(n-1)!$ 种方案。

当然,这里面有重复的,例如 $x=34543$,其中两个 $3$ 和两个 $4$ 的排列就是重复的,由于这两个 $3$ 无法区分,两个 $4$ 无法区分,方案数要除以 $2!2!$。

综上,排列个数为

$$
\dfrac{(n-\textit{cnt}0)\cdot (n-1)!}{\prod\limits{i=0}^{9}\textit{cnt}_i!}
$$

加入答案。

具体请看 视频讲解 第三题,欢迎点赞关注~

###py

class Solution:
    def countGoodIntegers(self, n: int, k: int) -> int:
        fac = [factorial(i) for i in range(n + 1)]
        ans = 0
        vis = set()
        base = 10 ** ((n - 1) // 2)
        for i in range(base, base * 10):  # 枚举回文数左半边
            s = str(i)
            s += s[::-1][n % 2:]
            if int(s) % k:  # 回文数不能被 k 整除
                continue

            sorted_s = ''.join(sorted(s))
            if sorted_s in vis:  # 不能重复统计
                continue
            vis.add(sorted_s)

            cnt = Counter(sorted_s)
            res = (n - cnt['0']) * fac[n - 1]
            for c in cnt.values():
                res //= fac[c]
            ans += res
        return ans

###java

class Solution {
    public long countGoodIntegers(int n, int k) {
        int[] factorial = new int[n + 1];
        factorial[0] = 1;
        for (int i = 1; i <= n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }

        long ans = 0;
        Set<String> vis = new HashSet<>();
        int base = (int) Math.pow(10, (n - 1) / 2);
        for (int i = base; i < base * 10; i++) { // 枚举回文数左半边
            String s = Integer.toString(i);
            s += new StringBuilder(s).reverse().substring(n % 2);
            if (Long.parseLong(s) % k > 0) { // 回文数不能被 k 整除
                continue;
            }

            char[] sortedS = s.toCharArray();
            Arrays.sort(sortedS);
            if (!vis.add(new String(sortedS))) { // 不能重复统计
                continue;
            }

            int[] cnt = new int[10];
            for (char c : sortedS) {
                cnt[c - '0']++;
            }
            int res = (n - cnt[0]) * factorial[n - 1];
            for (int c : cnt) {
                res /= factorial[c];
            }
            ans += res;
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    long long countGoodIntegers(int n, int k) {
        vector<int> factorial(n + 1);
        factorial[0] = 1;
        for (int i = 1; i <= n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }

        long long ans = 0;
        unordered_set<string> vis;
        int base = pow(10, (n - 1) / 2);
        for (int i = base; i < base * 10; i++) { // 枚举回文数左半边
            string s = to_string(i);
            s += string(s.rbegin() + (n % 2), s.rend());
            if (stoll(s) % k) { // 回文数不能被 k 整除
                continue;
            }

            ranges::sort(s);
            if (!vis.insert(s).second) { // 不能重复统计
                continue;
            }

            int cnt[10]{};
            for (char c : s) {
                cnt[c - '0']++;
            }
            int res = (n - cnt[0]) * factorial[n - 1];
            for (int c : cnt) {
                res /= factorial[c];
            }
            ans += res;
        }
        return ans;
    }
};

###go

func countGoodIntegers(n, k int) (ans int64) {
factorial := make([]int, n+1)
factorial[0] = 1
for i := 1; i <= n; i++ {
factorial[i] = factorial[i-1] * i
}

vis := map[string]bool{}
base := int(math.Pow10((n - 1) / 2))
for i := base; i < base*10; i++ { // 枚举回文数左半边
x := i
t := i
if n%2 > 0 {
t /= 10
}
for ; t > 0; t /= 10 {
x = x*10 + t%10
}
if x%k > 0 { // 回文数不能被 k 整除
continue
}

bs := []byte(strconv.Itoa(x))
slices.Sort(bs)
s := string(bs)
if vis[s] { // 不能重复统计
continue
}
vis[s] = true

cnt := [10]int{}
for _, c := range bs {
cnt[c-'0']++
}
res := (n - cnt[0]) * factorial[n-1]
for _, c := range cnt {
res /= factorial[c]
}
ans += int64(res)
}
return
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(10^m\cdot n\log n)$,其中 $m = \left\lfloor\dfrac{n-1}{2}\right\rfloor$。
  • 空间复杂度:$\mathcal{O}(10^m\cdot n)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/最短路/最小生成树/二分图/基环树/欧拉路径)
  7. 动态规划(入门/背包/状态机/划分/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

枚举 + 组合数学

作者 mipha-2022
2024年9月1日 00:15

Problem: 3272. 统计好整数的数目

[TOC]

思路

枚举

回文,很容易想到折半枚举,n最大10位,折半枚举最大就5位,所以:

  • 枚举回文左边
  • 镜像得到右边
  • 拼接成完整数字,再判断该数字能否被k整除
        # 枚举
        n_half = (n+1) // 2
        start = 10**(n_half-1)
        end = 10**n_half
        dt = set()
        for num in range(start,end,1):
            l = str(num)
            if n&1:
                r = l[:-1][::-1]
            else:
                r = l[::-1]
            
            s = l+r
            if int(s) % k == 0:
                # 去重
                dt.add(''.join(sorted(s)))

组合数学

将第一步得到的数字打乱,看可以得到多少个没有前缀0的整数。

  • 枚举非0头位
  • 剩下的位置塞入其它数字
        res = 0
        # 组合数学
        for s in dt:
            # 计数
            cnt = Counter(s)

            # 开头非0
            for w in '123456789':
                if cnt[w]:
                    cnt[w] -= 1
                    # 组合数学
                    t = 1
                    rest = n - 1
                    for lw in '0123456789':
                        # 剩余位中插入数字
                        t *= comb(rest,cnt[lw])
                        rest -= cnt[lw]
                    # 回溯
                    cnt[w] += 1
                    res += t

        return res

更多题目模板总结,请参考2023年度总结与题目分享

Code

###Python3

class Solution:
    def countGoodIntegers(self, n: int, k: int) -> int:
        '''
        打表
        折半 + 组合数字
        '''
        
        # 枚举
        n_half = (n+1) // 2
        start = 10**(n_half-1)
        end = 10**n_half
        dt = set()
        for num in range(start,end,1):
            l = str(num)
            if n&1:
                r = l[:-1][::-1]
            else:
                r = l[::-1]
            
            s = l+r
            if int(s) % k == 0:
                # 去重
                dt.add(''.join(sorted(s)))
        
        res = 0
        # 组合数学
        for s in dt:
            # 计数
            cnt = Counter(s)

            # 开头非0
            for w in '123456789':
                if cnt[w]:
                    cnt[w] -= 1
                    # 组合数学
                    t = 1
                    rest = n - 1
                    for lw in '0123456789':
                        if cnt[lw]:
                            t *= comb(rest,cnt[lw])
                            rest -= cnt[lw]
                    # 回溯
                    cnt[w] += 1
                    res += t

        return res
❌
❌