原题:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
在leetcode看到的算法,非常非常nice,在这里记录一下:
public int countDigitOne(int n) { int ones = 0; for (long m = 1; m <= n; m *= 10) ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0); return ones; }
思路分析:
1.从最外层的for循环可以看出,每次遍历的m分别表示数字n的个位、十位、百位等,一直到n的最高位
2. 假设n = 41324 我们以m=100为例
n/m = 413 ——-记作a
n%m = 24 ——-记作b
现在就将 原来的 n 划分为 ab两个部分。
现在我们就可以考虑百位的1 重复出现的次数了:
(1) 由于41324 原来的百位是 3 ,所以当 我们把百位数字置为1时,百位之前的数字共有 00—–42 即 a/10+1 (43)种可能
(2)对于上面的43种情况,分别 有 00—–99 (个位和十位) 共 m(此时m为100)种可能
(3)所以百位总共出现了 (a/10+1)*m次
3. 以m=1000重复上面的分析
n/m = 41 ——–a
n%m=324 ——–b
这种情形由于千位数字原来 就是1 ,所以 千位之前只有 0–3 共4种可能 ,这四种可能每种都可以乘以 1000(m),此时共有 a/10* m 次。
注意:当我们将千位之前的数字 置为最大 ,也就是万位取4, 千位之后只能 取 000—-324 ,共 b+1 次
所以 千位的1出现了 a/10* m +(b+1) 次。
到这里可以看出 当 a%10 >= 2,a/10需要+1: 可以这样实现 (a +8)/10
还需要判断 a%10 == 1 时 加上 b+1
也就是上面的这段代码:n/m = a, n%m=b
(n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0);