一道算法题,计算数字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
运算时间差了几千倍
降龙十巴掌,打完收工。感谢观看