整数中1出现的次数(从1到n的整数中1出现的次数)

时间:2024-01-14 10:32:20

题目

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

思考

方法1:

依次从 1 到 n 遍历,统计每个数中 1 出现的次数,然后累加起来。时间复杂度:遍历 n 个数,每次统计可以在 log(n) 时间内完成,因此 O(nlog(n))。

方法2:

新的角度:给定 N,统计“小于 N 的数在每一位上可能出现 1 的次数” 之和,可得到最终的解。使用函数 f(N) 表示。

1 位数的情况

如果 N=4,则所有可考虑的数为: 1,2,3,4 由此可得 f(N) = 1。不难看出对于所有 1<=N<=9, f(N) = 1,特殊的有 f(0)=0。

2 位数的情况

假设 N=14,可考虑的数为:1,2,3,4,5,6,7,8,9,10,11,12,13,14。

个位上出现 1 的情况:1,11; 2个

十位上出现 1 的情况:10, 11, 12, 13, 14;5个

综合:f(N)=个位数的情况 + 十位数的情况 = 2 + 5 = 7

一般的,

  • 个位上 1 的个数统计可以有两种情况:
    • 情况 1 : 当前数字 N 个位数是 0,则个位上 1 出现的次数为十位出现的数。(比如 N=10,个位上出现 1 的次数是 1)
    • 情况 2 :当前数字 N 个位数大于 0, 则个位上 1 出现的次数为十位出现的数加 1。(比如 N=14,个位上出现 1 的次数是 1+1=2,及 1 和 11)
  • 十位上 1 的个数统计:
    • 情况 1 : 当十位上的数为 1 时,则十位上出现 1 的次数为:个位上的数加 1 (比如 N=14, 十位上出现 1 的次数 = 4+1=5,及:10,11,12,13,14)
    • 情况 2 :当十位上的数大于 1 时,则十位上出现 1 的次数为 10 次。(比如 N=24, 十位上出现 1 的次数 = 10,及:10,11,12,13,14,15,16,17,18,19)

其他两位数字可以类似的方法很快计算出来

3 位数的情况

假设 N = 132

  • 个位上出现 1 的次数为 14 次:1,11,21,...,91,101,111,121,131
  • 十位上出现 1 的次数为 20 次:10~19, 110~119
  • 百位上出现 1 的次数为 33 次:100~132

一般化

同理可以计算 4,5,甚至 k 位数中 1 的个数。

一般的,假设有数 N = ak... a1

我们可以将数字分成 3 个部分,高位部分,当前计算的位置,低位部分;如果计算第 i 位上出现 1 的次数,则高位部分为 ak...ai+1,当前计算位置 ai,低位部分 ai-1...a1。而第 i 位上出现 1 的次数所受影响的来源也将分配到这 3 个部分当中。

在来一个例子,假设 N = 13014

  • 个位出现 1 的次数,ihight=1301, icur = 4,因此 icur > 1,所以 1 的次数为 ihight+1 = 1302
  • 十位出现 1 的次数,ihight=130, icur=1, ilow=4,因此 1 的次数为 ihight*10+ilow+1=130*10+4+1=1305
  • 百位出现 1 的次数,ihight=13, icur=0, ilow=14
    • icur = 0,影响只来源于高位部分,13 个 100,每个100在百位上将会出现100个1,也就是 13*100=1300
    • icur = 1,假设 N=13114,影响来源于高位和低位:
      • 高位部分,ihight=13, 13 个 100,则 1 的个数为 13 * 100 = 1300
      • 低位部分,ilow=14,13100~13114,1 的个数为 ilow+1 = 14+1 = 15
    • icur > 1,假设 N=13214,影响仅来源于高位:
      • ihight = 13,13 个 100,当前尾部 13100~13199 有一个100,总攻是 14 个,及 ihight+1 个 100,1 的个数为 (13+1)*100 = 1400

由此规律很容易实现代码。

思考总线,从简单的例子入手找规律,抽象出一般化的规律,要注意细节

code


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution{
public:
    int NumberOf1Between1AndN_Solution(int n){
        int icout = 0;
        int ihight = 0;
        int ilow = 0;
        int icur = 0;
        int ifactor = 1;

        while( n / ifactor != 0){
            ihight = n / (ifactor*10);
            icur = (n / ifactor) % 10;
            ilow = n - (n / ifactor) * ifactor;

            switch(icur){
                case 0 :
                    icout += ihight*ifactor;
                    break;
                case 1 :
                    icout += ihight*ifactor + ilow + 1;
                    break;
                default:
                    // icur > 1
                    icout += (ihight+1)*ifactor;

            }

            ifactor *= 10; // scan from the low-order to high-order
        }

        return icout;
    }
};

int main()
{
    return 0;
}

相关文章