「前端刷题」233.数字 1 的个数(HARD)

207 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

题目(Number of Digit One)

链接:https://leetcode-cn.com/problems/number-of-digit-one
解决数:569
通过率:48.6%
标签:递归 数学 动态规划 
相关公司:microsoft google amazon 

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

 

示例 1:

输入: n = 13
输出: 6

示例 2:

输入: n = 0
输出: 0

 

提示:

  • 0 <= n <= 109

思路

归纳法,对于个位出现的1:(n / 10) * 1 + (n % 10) >= 1 ? 1 : 0; 对于十位出现的1:(n / 100) * 10 + if (k > 19) 10 else if (k < 10) 0 else k - 10 + 1; 对于百位出现的1:(n / 1000) * 100 + if (k > 199) 10 else if (k < 100) 0 else k - 100 + 1; 最终归纳出: (n / (i * 10)) * i + if (k > 2 * i - 1) i else if (k < i) 0 else k - i + 1, 其中k = n % (i * 10);

代码

var countDigitOne = function(n) {
    let count = 0;

    for (let i = 1; i <= n; i *= 10) {
        let divide = i * 10;
        let p = Math.floor(n / divide), k = n % divide, rest = 0;

        count += p * i;
        rest = (k > (2 * i - 1)) ? i : ((k < i) ? 0 : k - i + 1);
        count += rest;
    }
    return count;
};

思路2

数学题解,枚举每一数位上 1 的个数

function countDigitOne(n: number): number {
    let count = 0;

    for (let i = 1; i <= n; i *= 10) {
        const divide = i * 10;
        const p = Math.floor(n / divide),
            k = n % divide;
        let rest = 0;

        count += p * i;
        rest = k > 2 * i - 1 ? i : k < i ? 0 : k - i + 1;
        count += rest;
    }
    return count;
}

其他思路

主要的解题思路是分为3种情况:

  1. 当前位数值等于0场景 举个例子:201 十位是0的场景,把十位置为1,看百位、个位,即:010~119(十位为1时,百位不可能为2);个数为2 * 10 = 20,2为百位数值(0和1两种情况),10位当前位阶十位;

  2. 当前位数值等于1场景 举个例子:213 十位是1的场景,把十位置为1,看百位、个位,即:010~213(十位为1时,百位不可能为2);个数为2 * 10 + 13 + 1 = 33,2为百位数值(0和1两种情况),10位当前位阶十位,13 + 1为210~213的数量;

  3. 当前位数值大于1场景 举个例子:263 十位是大于1的场景,把十位置为1,看百位、个位,即:010~219(十位为1时,百位不可能为2);个数为2 * 10 + 10 = 30,2为百位数值(0和1两种情况),10位当前位阶十位,10为当前位阶,即210~219的数量;

代码

/**
 * @param {number} n
 * @return {number}
 */
var countDigitOne = function(n) {
    let x = 1;
    let count = 0;
    for(let i = 0; n >= x; i+=1){
        let s = Math.floor(n%(x*10)/x);
        // 当前位是0的情况
        if( s === 0){
            // 左侧相邻高一位数 * 当前位阶
            count += Math.floor(n/(x*10)) * x;
        }
        // 当前位是1的情况
        if(s === 1){
            // 左侧相邻高一位数 * 当前位阶 + 右侧所有低位数 + 1
            count += Math.floor(n/(x*10)) * x + n%x + 1; 
        }
        // 当前位是大于1的情况
        if(s > 1){
            // 左侧相邻高一位数 * 当前位阶 + 当前位阶
            count += Math.floor(n/(x*10)) * x + x;
        }
        x *= 10
    }
    return count;
}