统计数字
问题描述
编程统计数字 k 在 0 到 n 中的出现的次数,k 可能是 0~9 的一个值。
输入
有多组数据。每组数据有一行,该有一个整数n和一个0到9之间的数字k。
输出
对每组数据,一行输出数字k在0到n中出现的次数。
输入样例
12 1
输出样例
5
最简单的算法显然是暴力枚举,
但也可以规律构造:
先考虑1~9中的数字情况,分别考虑n的每一位数字为k的数字会有多少个,计算贡献。
举个简单栗子,n = 233211, k = 2;从小到大枚举每一位,有三种情况:
1)当前位i数字小于k
十位数字上:i=2, cur = 1 < k, 高位部分数字 h = 2332,低位数字 l = 1。那么十位数字为2的情况: 20 ~ 29, 120 ~ 929, …, 1020 ~ 9929,233120~233129,。
也就是是固定个位为2,高位数字从0 ~ 2331中取值,低位数字从0 ~ 9中取值,个位为2对答案的贡献就是:2332 * 10;
贡献可概括为: h * 10 ^ (i - 1)
2)当前位i数字等于k
枚举到百位,i = 3,cur = 2 = k,h = 233,低位数字 l = 11,那么百位为1的数字可能取值为:200 ~ 299,1200 ~ 9299,…, 232200 ~ 23299,这部分和前面cur < k的情况类似,但还有一部分233200 ~ 233311, 共11个,多出部分的数字等于低位数字(l = 11)+1
贡献可概括为: h * 10 ^ (i -1) + l + 1;
3)当前位i数字大于k
枚举到千位,i = 4,cur = 3 > k,h = 23,低位数字 l = 211,那么千位为1的数字可能取值为:2000 ~ 2999,12000 ~ 92990,…, 232200 ~ 2322999;也就是是固定千位为2,高位数字从0 ~ 22中取值,低位数字从0 ~ 999中取值,千位为2对答案的贡献就是:23 * 1000;
贡献可概括为: (h + 1) * 10 ^ (i -1) ;
再考虑k = 0的特殊情况:
k = 0 特殊地方在于其最高位数字不能为0,
同上 n = 233211, k = 0;
枚举十位,固定十位数字为0,此时h = 2332,l = 1,由于最高位不能为0,所以高位取值变为1 ~ 2331,低位数字取值0~9,也就是说高位部分贡献h比k > 0部分少1。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll solve(ll n, int k){
if(k == 0 && n == 0) return 1; // 0 0
ll l, h, cur, pos = 1, ans = 0;
while(n / pos > 0){
if(k == 0 && n / (pos * 10) == 0){ //当前位为0,最高位数字不能为0
++ans; //注意ans++,这里算的是特殊数字0
break ;
}
cur = (n / pos) % 10;
l = n - (n / pos) * pos;
h = n / (pos * 10);
if(k == 0) --h;//k = 0,高位数字贡献减一
if(cur < k){
ans += h * pos;
}
else if(cur == k){
ans += h * pos + l + 1LL;
}
else{
ans += (h + 1) * pos;
}
pos *= 10;
}
return ans;
}
ll bfsolve(ll n,int k){
ll ans = (0 == k);
for(ll i = 1; i <=n; ++i){
ll tmp = i;
while(tmp){
if(tmp % 10 == k) ++ans;
tmp /= 10;
}
}
return ans;
}
int main(){
int k;
ll n;
while(cin >> n >> k){
cout << solve(n, k) << endl;
cout << bfsolve(n,k) << endl;
}
return 0;
}