描述
中文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];
}
};