题目描述:

求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。

解题思路:

【一个明显规律】:

  • 从 1 至 10,在它们的个位数中,任意的 1 都出现了 1 次。
  • 从 1 至 100,在它们的十位数中,任意的 1 都出现了 10 次。
  • 从 1 至 1000,在它们的百位数中,任意的 1 都出现了 100 次。
  • 依此类推,从 1 至 10i,在它们的左数第二位(右数第 i 位)中,任意的1 都出现了10i−1次。

我们把当前数n分成两个部分,例如当用m=100来把n=3141592分成a=31415和b=92两个部分,求百位出现1的次数。此时把从1到3141592的数中,百位为1的数看做oooo1xx,其中oooo可以从0到3141,xx可以从0到99。因此百位的1出现3142*100次,也就是(a/10+1)*m。

30、从1到n整数中1出现的次数_剑指offer

再把m取1000判断千位上1出现的次数,同样的把n分成a=3141和b=592两个部分。把从1到3141592的数中,千位为1的数看做ooo1xxx,当ooo从0到313,xxx可以从0到999,当ooo是314的时候,xxx只可以从0到592了。因此千位的1出现313*1000+593次,也就是(a/10)*m+(b+1)。

30、从1到n整数中1出现的次数_剑指offer_02

因此我们可以知道当a的个位为2及以上时,次数是(a/10+1)*m。a的个位为1时,次数是(a/10)*m+(b+1)。a的个位为0时,次数是(a/10)*m。综上三种情况可以得到(a + 8) / 10 * m + (a % 10 == 1) ? (b + 1):0;

Demo:

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int count = 0;
        for (int m = 1; m <= n; m *= 10) 
        {
            int a = n / m,b = n % m;
            count += (a + 8) / 10 * m + ((a % 10 == 1) ? b + 1 : 0);
        }
        return count;
    }
};