蓝桥杯冲刺训练营之递归——例5.统计好数字的数目

1. 题目

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

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

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

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

示例 1:

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

示例 2:

输入:n = 4
输出:400

示例 3:

输入:n = 50
输出:564908303

提示:

1 ≤ n ≤ 1 0 15 {1\leq n\leq10^{15}} 1n1015

2. 解题思路

这一题乍看很像暴力求解,但是再看数据量呢,远远超过 1 0 7 {10^7} 107,用暴力必然会超时。

仔细分析题目的条件,偶数位上满足是偶数,奇数位上满足是质数,同时包括首位是0的情况,那这不就是简单的乘法吗?

偶数位要求是偶数,单个数字是偶数的就5个,0,2,4,6,8
奇数位要求是质数,单个数字是质数的就4个,2,3,5,7

所以要求n位数的好数有多少个,不就是数有多少个偶数位和多少个奇数位吗,每个偶数位有5种情况(选择0,2,4,6,8中的某一个,所以有5种选择),每个奇数位有4种情况(2,3,5,7)。所以一共满足条件的数的个数就满足如下关系:

l e n _ o f _ e v e n {len\_of\_even} len_of_even代表偶数位个数
l e n _ o f _ o d d {len\_of\_odd} len_of_odd代表奇数位个数
总个数 t o t a l = 5 l e n _ o f _ e v e n ∗ 4 l e n _ o f _ o d d {total = 5^{len\_of\_even}*4^{len\_of\_odd}} total=5len_of_even4len_of_odd

这题就这么简单吗?看看数据量呢, 1 ≤ n ≤ 1 0 15 {1\leq n\leq10^{15}} 1n1015,无论是用for循环还是普通递归去计算都会超时或者 s t a c k − o v e r f l o w {stack-overflow} stackoverflow。所以这题应该用快速幂。在我的另一篇文章中有详细讲解(蓝桥杯冲刺训练营之递归——例2.pow函数)。

对于上述公式可以进一步进行化简成20的幂函数,只需要判断n是奇数还是偶数,通过简单推到可得到如下公式:

t o t a l = { 5 n = = 1 2 0 n / 2 n 是 偶 数 5 ∗ 2 0 n / 2 n 是 奇 数 total=\left\{ \begin{array}{lcl} 5 & & {n == 1}\\ 20^{n/2} & & { n是偶数} \\ 5*20^{n/2} & & {n是奇数} \\ \end{array} \right. total=520n/2520n/2n==1nn

3. 代码

class Solution {
public:

    long long  NUM = 1000000007;

    map<long long, long long > table;

    long long myPow(long long x, long long n){

        if (n == 0) return 1;
        if (n == 1) return x;

        long long ans;

        //奇数 = x*x^(n/2)*x^(n/2)
        //偶素 = x^(n/2)*x^(n/2)

        if(n%2==0){
            if(table.find(n/2) == table.end()){
                table[n/2] = myPow(x, n/2)%NUM;
            }

            ans = table[n/2]*table[n/2]%NUM;
            table[n] = ans;
        }
        else{

            if(table.find(n/2) == table.end()){
                table[n/2] = myPow(x, n/2)%NUM;
            }

            ans = x*table[n/2]%NUM*table[n/2]%NUM;
            table[n] = ans;
        }

        return ans;
    }
    
    int countGoodNumbers(long long n) {

        long long ans;

        if (n == 1) return 5;

        //奇数是pow(20,n/2)*5
        //偶数是pow(20, n/2)

        if(n%2!=0){
            ans = myPow(20, n/2)*5%NUM;
        }
        else{
            ans = myPow(20, n/2)%NUM;
        }

        return ans%NUM;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zh4men9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值