加载中...
面试题 17.06-2出现的次数(Number Of 2s In Range LCCI)
发表于:2021-12-03 | 分类: 困难
字数统计: 892 | 阅读时长: 4分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/number-of-2s-in-range-lcci

英文原文

Write a method to count the number of 2s that appear in all the numbers between 0 and n (inclusive).

Example:

Input: 25
Output: 9
Explanation: (2, 12, 20, 21, 22, 23, 24, 25)(Note that 22 counts for two 2s.)

Note:

  • n <= 10^9

中文题目

编写一个方法,计算从 0 到 n (含 n) 中数字 2 出现的次数。

示例:

输入: 25
输出: 9
解释: (2, 12, 20, 21, 22, 23, 24, 25)(注意 22 应该算作两次)

提示:

  • n <= 10^9

通过代码

高赞题解

解题思路

leetcode.jpg

主要思路是数位dp:
以dp[i]表示n的1~i位组成的数字所包含2的个数,关键点在于推导出dp[i]与dp[i-1]的关系

例如:n = 3478

dp[1] == numberOf2sInRange(8)   
dp[2] == numberOf2sInRange(78)
dp[3] == numberOf2sInRange(478)
dp[4] == numberOf2sInRange(3478)

dp[i] = f(dp[i-1]) ? 

下面来分析一下dp[i]与dp[i-1]的关系
根据第i位的取值可分为4种情况:

  1. 第i位是0
    例如:n = 102, 分析dp[2]和dp[1]的关系,即numberOf2sInRange(02)与numberOf2sInRange(2) (02实际是2,写作02便于理解)
    第i位是0,该位取值范围只有这一种可能,由此可得
    dp[2] = dp[1] 
    numberOf2sInRange(02) = numberOf2sInRange(2)
  1. 第i位是1
    例如:n = 178,分析dp[3]和dp[2]的关系,即numberOf2sInRange(178)与numberOf2sInRange(78)
    第3位是1,该位可能取0,1两种情况:

    dp[3] = 当第3位是01-2位取00~992的次数 + 当第3位是1, 1-2位取00~782的次数
    dp[3] = numberOf2sInRange(99) + dp[2]
    numberOf2sInRange(178) = numberOf2sInRange(99) + numberOf2sInRange(78)
  2. 第i位是2
    例如:n = 233, 分析dp[3]和dp[2]的关系,即numberOf2sInRange(233)与numberOf2sInRange(33)

    dp[3] =3位取0-1,1-2位取00~992的次数 +3位是2,1-2位取00~3321-2位出现的次数 +3位是2,1-2位取00~332在第3位出现的次数
    dp[3] = 2 *numberOf2sInRange(99) + dp[2] + 33 + 1
    numberOf2sInRange(233) = 2 * numberOf2sInRange(99) + numberOf2sInRange(33) + 33 + 1
  3. 第i位大于2
    以 n = 478为例,分析dp[3]和dp[2]的关系,即numberOf2sInRange(478)与numberOf2sInRange(78)

    dp[3] =3位取0-3,1-2位取00-992出现在1-2位的次数 +3位取4,1-2位取00-782的次数 +3位取2,1-2位取00-992出现在第3位的次数
    dp[3] = 4 * numberOf2sInRange(99) + dp[2] + 100

总结上面4种情况:

dp[i]与dp[i-1]的关系,假设n的第i位的值为k
dp[i] = k * numberOf2sInRange(99..9){共i-19} + dp[i-1] + {n % 10^(i-1) + 1 }{若k == 2}  + { 10^(i-1) } {若k > 2}

根据递推公式可以发现,若计算dp[i],不仅要知道dp[i-1]还要知道numberOf2sInRange(99..9),所以要同时计算numberOf2sInRange(99..9)的值

代码

class Solution {
    public int numberOf2sInRange(int n) {
        if(n == 0) {
            return 0;
        }
        int digit = (int)Math.log10(n) + 1;
        int[][] dp = new int[digit+1][2];  
        // dp[i][0] = numberOf2sInRange(n % pow(10, i)) 保存0~n的1-i位组成的数包含2的个数
        // dp[i][1] = numberOf2sInRange(99..9) 保存i位均为9包含2的个数
        dp[1][0] = n % 10 >= 2 ? 1:0;
        dp[1][1] = 1;
        for(int i = 2; i <= digit; i++) {
            int k = n/ ((int)Math.pow(10, i-1)) % 10;
            dp[i][0] = k * dp[i-1][1] + dp[i-1][0];
            if(k == 2) {
                dp[i][0] += n % (int)Math.pow(10, i-1) +1; 
            } else if(k > 2){
                dp[i][0] +=  (int)Math.pow(10, i-1);
            }
            dp[i][1] = 10 * dp[i-1][1] + (int)Math.pow(10, i-1); //计算1-i位均为9的值包含2的个数
        }
        return dp[digit][0];
    }
}

统计信息

通过次数 提交次数 AC比率
4960 10961 45.3%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
面试题 17.04-消失的数字(Missing Number LCCI)
下一篇:
面试题 17.07-婴儿名字(Baby Names LCCI)
本文目录
本文目录