LintCode 3. 统计数字

描述

中文English

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

您在真实的面试中是否遇到过这个题?  是

题目纠错

样例

样例 1:

输入:
k = 1, n = 1
输出:
1
解释:
在 [0, 1] 中,我们发现 1 出现了 1 次 (1)。

样例 2:

输入:
k = 1, n = 12
输出:
5
解释:
在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 中,我们发现 1 出现了 5 次 (1, 10, 11, 12)(注意11中有两个1)。
举个例子:
    比如: n = 4351 求 从 1 到 4351 这个数字中0-9每个数字出现的次数是多少?
    
    先不考虑去除前导0,有这样的规律: 对于n位的数字: 0 - 9 各个数字出现的次数是: n*10^(n - 1)
    
    可以先考虑千位,有4个 0 - 999 区间而每个区间中 0 - 9 各个数字出现的次数是: n*10^(n - 1) (n = 3)
    然后计算千位出现的次数: 当千位是0的时候: 千位上的0出现了1000次(0000-0999),千位上的1出现了1000次(1000-1999),千位上的2出现了1000次(2000-2999),千位上的3出现了1000次(3000-3999),千位上的4出现了352次(4000-4351)
    然后是考虑百位(351),递归的调用函数就可以了。

    最后去除前导零:
    例如: 3位数的前导零 —— —— —— 当第一位是0的时候,后面的数有100个,因此3位数的前导0要去除100个。  


    注意的是:
    c = pow(a,b) 中的返回结果c是double,pow()返回double类型数据影响了取余操作因为 int 不能和double 进行 % 操作,不想麻烦的转换数据类型自己手写了一个pow()函数

    计算数字num的长度,如 1234 的长度是 4 可以用log10(num) + 1 获得数字的长度,缺点是对于数字0不能应用,num需要大于0 
     
class Solution {
public:
    /**
     * @param k: An integer
     * @param n: An integer
     * @return: An integer denote the count of digit k in 1..n
     */
     int cnt[10];
     
     int pow(int a,int k){
         int s = 1;
         for (int i = 0; i < k; ++i)
            s *= a;
        return s;
     }
     
     void fun(int n){
         int len = log10(n) + 1;
         int p = n /pow(10,len-1);
         for (int i = 0; i < 10; ++i){
             cnt[i] += p*(len - 1)*pow(10,len - 2);
         }
         for (int i = 0; i < p; ++i){
             cnt[i] += pow(10,len - 1);
         }
         int t = (n % pow(10,len - 1));
         if (t == 0){  // 如果是2000 的话,算了千位后就不用再算百位及其之后的了,因为全是0,而它的之前的数已经在算千位的时候算过了,因此不用算了
             cnt[p]++;
             cnt[0] += len - 1;
             return;
         }
         int lenT = log10(t) + 1;  
         if (lenT + 1 != len){  // 如果是 2020 这种不要忘了两个2之间的0
             cnt[0] += (len - lenT - 1)*(t + 1);
         }
         cnt[p] += 1 + t; // 最高位数字出现的次数
         return fun(t);
     }
     
    int digitCounts(int k, int n) {
        memset(cnt,0,sizeof(cnt));
        if (!n){
            cnt[0] += 1;
            return cnt[k];
        }
        fun(n);
        int len = log10(n) + 1;
        for (int i = 1; i < len; ++i){  // 数据从0开始,因此个位的前导0不能去了,要保留着。
            cnt[0] -= pow(10,i);  // 两位数中要去除的前导0有10个,分别是 00,01,02,03,...,09 每个中最前面的一个0
        }
        return cnt[k];
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值