文章

[LeetCode] 每日一题 1922. 统计好数字的数目(快速幂)

题目描述

我们称一个数字字符串是 好数字 当它满足(下标从 0 开始)偶数 下标处的数字为 偶数 且 奇数 下标处的数字为 质数 (235 或 7)。

  • 比方说,"2582" 是好数字,因为偶数下标处的数字(2 和 8)是偶数且奇数下标处的数字(5 和 2)为质数。但 "3245" 不是 好数字,因为 3 在偶数下标处但不是偶数。

给你一个整数 n ,请你返回长度为 n 且为好数字的数字字符串 总数 。由于答案可能会很大,请你将它对 109 + 7 取余后返回 。

一个 数字字符串 是每一位都由 0 到 9 组成的字符串,且可能包含前导 0 。

题目链接

https://leetcode.cn/problems/find-the-count-of-good-integers

示例输入

示例 1

输入:n = 1
输出:5
解释:长度为 1 的好数字包括 "0","2","4","6","8" 。

示例 2

输入:n = 4
输出:400

示例 3

输入:n = 50
输出:564908303

提示

  • 1 <= n <= 10^15

解题思路

这题一读题就能看出思路其实非常直接:统计满足特定规则的数字字符串总数

规则如下:

  • 偶数下标(0, 2, 4...)必须是 偶数(可选数字:0, 2, 4, 6, 8,共5个);

  • 奇数下标(1, 3, 5...)必须是 质数(可选数字:2, 3, 5, 7,共4个);

假设长度为 n,那么只要分别统计偶数位和奇数位上各自的组合数量,相乘即可。

于是,答案为:
5^(偶数位个数) * 4^(奇数位个数)

也就是:

5^((n+1)/2) * 4^(n/2)

然而 n 最大可达 10^15,显然不能直接用 Math.pow 计算。因此,我们需要使用 快速幂算法 进行高效求解。

快速幂的核心思想是将指数 n 拆成若干个二进制位,用递归或迭代的方式,把乘法次数压缩到 O(log n),极大提升效率。

我自己之前在 https://www.nxcoding.com/archives/kuai-su-mi-suan-fa 中系统总结过快速幂算法,大家感兴趣的可以点进去看看,有详解原理和代码模板~

代码实现

class Solution {
    int mod = 1000000007;

    public int countGoodNumbers(long n) {
        return (int)(pow(5, (n + 1) / 2) * pow(4, n / 2) % mod);
    }

    private long pow(int num, long n) {
        long res = 1;
        long mul = num;
        while (n > 0) {
            if (n % 2 != 0) {
                res = res * mul % mod;
            }
            mul = mul * mul % mod;
            n /= 2;
        }
        return res;
    }
}

复杂度分析

  • 时间复杂度: O(log n)
    每次调用 pow 函数时最多进行 log n 次乘法,两个幂次各自调用一次

  • 空间复杂度: O(1)
    所有变量为基本数据类型,没有额外空间开销

🚀快速幂真的是处理大数乘法类题目的利器,在这种指数级爆炸的地方显得尤为重要!

总结

这道题本质上是数学题,思路简洁却非常典型,是快速幂算法的完美应用场景。虽然逻辑看起来简单,但背后的指数拆分、模运算都值得好好体会,是我在刷题中非常喜欢的一类题型。

如果你对快速幂还不熟,强烈建议了解一下快速幂的思路,这种思路在很多场景中都能派上大用场💡

希望这篇分享能为你带来启发!如果你有任何问题或建议,欢迎在评论区留言,与我共同交流探讨。

License:  CC BY 4.0