美文网首页
算法:计算数字k在0到n中的出现的次数,k可能是0~9的一个值

算法:计算数字k在0到n中的出现的次数,k可能是0~9的一个值

作者: jiangji | 来源:发表于2019-09-30 11:49 被阅读0次

    一道算法题,计算数字k在0到n中的出现的次数,k可能是0~9的一个值

    要求:计算数字k在0到n中的出现的次数,k可能是0~9的一个值

    (只写原创的小菜鸟一枚)

    老规矩,还是直接先上干货

    function f (n, k) {
        let digit = String(n).length
        let count = 0, n2 = String(n)
        while (digit > 2) { // while里面处理3位数以上的(就是大于等于100)
            let base = (digit - 1) * 10 ** (digit - 2)
            let firstNum = +n2.substr(-digit, 1)
            if (firstNum === k) {
                count += +n2.substr(-(digit - 1))
                count++
            } else if (firstNum > k) {
                count += 10 ** (digit - 1)
            }
            digit--
            count += firstNum * base
        } // 之后的处理99到0
        if (digit === 2) {
            let firstNum = +n2.substr(-digit, 1)
            if (firstNum > k) {
                count += 10
            } else if (firstNum === k) {
                let lastNum = +n2.substr(-1, 1)
                count += lastNum + 1
            }
            count += firstNum
        }
        if (+n2.substr(-1) >= k) count++
        return count
    }
    

    看懂的小伙伴就不用看下面的思路了

    先来个最笨拙的办法,也是最简单实用的

    function ff (n, k) { // 这么简单的,就不用注释了吧
      let str = ''
      for (let i = 0; i <= n; i++) {
        str += i
      }
      let count = 0
      for(let i = 0; i < str.length; i++) {
        if (+str[i] === k) {
          count++
        }
      }
      return count
    }
    

    算法就是发现规律,用上自己的思维总结方法

    下面开始摆上自己的思考的过程,先说一声,非常墨迹,非常墨迹,非常墨迹

    自己不想每一位数字都遍历,这样太慢了,也上网查了一些的算法,大多是高位数,当前位数,低位数,代码是断了,但是不方便理解。就想着用自己思维思考一下。

    (吐槽下,各种论坛,社区各种抄袭,或者照搬算法书上的,骗浏览量,能不能自己写一下自己的思路,代码开源是干嘛的?抄袭代码?是交流思维和逻辑的)

    好了,开始说思路

    思路是不管高位数,当前位数,低位数,我只看当前这一位数的数字,减少一些复杂度

    直接用上面的第二个函数ff来看结果就行了
    ff(1, 2)       // 0
    ff(10, 2)      // 1
    ff(100, 2)     // 20
    ff(1000, 2)    // 300
    ff(10000, 2)   // 4000
    ff(100000, 2)  // 50000
    ff(1000000, 2) // 600000
    ...
    

    本人就发现一个规律

    3位数或者3位数以上的规律:
    位数用digit来表示 例如10是2位数,那么digit=2; 9999是4位数,那么digit=4
    100     是 3位数 ,结果是20          20 = 2乘以(10的1次方) (digit - 1) * Math.pow(10, digit - 2)
    1000    是 4位数 ,结果是300        300 = 3乘以(10的2次方) (digit - 1) * Math.pow(10, digit - 2)
    10000   是 5位数 ,结果是4000      4000 = 4乘以(10的3次方) (digit - 1) * Math.pow(10, digit - 2)
    100000  是 6位数 ,结果是50000    50000 = 5乘以(10的4次方) (digit - 1) * Math.pow(10, digit - 2)
    1000000 是 7位数 ,结果是600000  600000 = 6乘以(10的5次方) (digit - 1) * Math.pow(10, digit - 2)
    

    发现以上规律

    可以写出  (n - 1) * Math.pow(10, n - 2)
    

    再来几个另外的数字,再找找3位数和3位数以上的数字的规律

    ff(100, 2) // 20
    ff(200, 2) // 41
    ff(300, 2) // 160
    ff(400, 2) // 180
    ff(500, 2) // 200
    ff(600, 2) // 220
    

    不难发现

    1.当百位数的数字不等于k时候,结果是百位数上的数字乘以(digit - 1) * Math.pow(10, digit - 2)

    2.当百位数的数字等于k时,结果是上一点的结果,然后再加1(因为百位数上的数字是k,所以多出现了一次)

    3.当百位数上的数字大于k时,再加上Math.pow(10, digit - 1)(例如,n=300, k = 2,那么200, 201, 202...一直到299,百位数上的数字都是2)
    现在写下目前发现的规律

    10 ** 2就是Math.pow(10, 2),不要纠结
    function f (n, k) {
        let count = 0
        let digit = String(n).length
        let n2 = String(n)                         // 把数字n转为字符串,方便根据下标查找firstNum
        let base = (digit - 1) * 10 ** (digit - 2) // base就是百位数上的数字为1的时候
        let firstNum = +n2.substr(-digit, 1)       // firstNum是百位数上的数字
        if (firstNum === k) {
            count += +n2.substr(-(digit - 1))
            count++
        }
        else if (firstNum > k) count += 10 ** (digit - 1)
        count += firstNum * base
        return count
    }
    

    写好了之后我们来试一下

    f(100, 2) // 20
    f(200, 2) // 41
    f(300, 2) // 160
    f(400, 2) // 180
    f(500, 2) // 200
    f(600, 2) // 220
    

    结果和我们想象的一样,完美,接下来就是处理99到0的数字了
    剩下的数字不多,我们直接用思维推理

    先处理个位数

    lastNum是个位数,k是一位数
    lastNum 小于 k, 结果是0
    lastNum 不小于 结果是1
    写下代码
    if (lastNum >= k) count++
    

    再处理十位数的数字

    (这里用firstNum代替十位数的数字,用b代替个位数的数字)
    如果firstNum小于k,例如24和3,满足的数字有13,23                 count += 2 。(3这个数不管,因为放到个位数的逻辑中去处理)
    如果firstNum等于k,例如34和3,满足的数字有13,23,30,31,32,33,34  count += 8。 (3这个数不管,因为放到个位数的逻辑中去处理,33是2次)
    如果firstNum大约k,例如43和3,满足的数字有13,23,30,31,32,33,34,35,36,37,38,39,43  count += 14。 (3这个数不管,因为放到个位数的逻辑中去处理,33是2次)
    写下代码
    // n2是数字转成字符串,方便根据下标查找对应位数的数字
    let firstNum = +n2.substr(-digit, 1)
    let lastNum = +n2.substr(-1, 1)
    if (firstNum > k) {
      count += 10
    } else if (firstNum === k) {
      count += lastNum + 1
    }
    count += firstNum
    

    最后把所有的逻辑整合

    function f (n, k) {
        let digit = String(n).length
        let count = 0, n2 = String(n)
        while (digit > 2) { // while里面处理3位数以上的(就是大于等于100)
            let base = (digit - 1) * 10 ** (digit - 2)
            let firstNum = +n2.substr(-digit, 1)
            if (firstNum === k) {
                count += +n2.substr(-(digit - 1))
                count++
            } else if (firstNum > k) {
                count += 10 ** (digit - 1)
            }
            digit--
            count += firstNum * base
        } // 之后的处理99到0
        if (digit === 2) {
            let firstNum = +n2.substr(-digit, 1)
            if (firstNum > k) {
                count += 10
            } else if (firstNum === k) {
                let lastNum = +n2.substr(-1, 1)
                count += lastNum + 1
            }
            count += firstNum
        }
        if (+n2.substr(-1) >= k) count++
        return count
    }
    
    时间比较
    ff(9999999, 7) // 结果是7000000,用时2953ms
    f(9999999, 7)  // 结果是7000000,用时3ms
    运算时间差了几千倍
    

    降龙十巴掌,打完收工。感谢观看

    相关文章

      网友评论

          本文标题:算法:计算数字k在0到n中的出现的次数,k可能是0~9的一个值

          本文链接:https://www.haomeiwen.com/subject/mrkjyctx.html