问题 E: 统计数字问题
题目描述
一本书的页码从自然数 1 开始顺序编码直到自然数 n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字 0。例如,第 6 页用数字 6 表示,而不是 06 或 006 等。数字计数问题要求对给定书的总页码 n,计算出书的全部页码中分别用到多少次数字 0, 1, 2, \dots, 9。给定表示书的总页码的10 进制整数n (1 \le n \le 10^7)。
输入
输入数据只有
1
1
1 行,给出表示书的总页码的整数
n
n
n
输出
输出数据共有
10
10
10 行,在第
k
k
k 行输出页码中用到数字
k
−
1
k-1
k−1 的次数,
k
=
1
,
2
,
…
,
10
k = 1, 2, \dots, 10
k=1,2,…,10。
输入输出样例
样例输入
11
样例输出
1
4
1
1
1
1
1
1
1
1
详解
最直观的方法,从一到n罗列,拆数,0-9计数器叠加,不过n<=10^7,时间可能超限,我觉得用数学计算也能解决,我们以238为例,依次去看每个数在每个位置上出现的次数。
-
在个位上,8出现在 8,18,28,38,48,58,68,78,88,98,108,118,128,138,148,158,168,178,188,198,208,218,228,238,一共24次,其他小于8的数同上也是24次,而大于8的数只出现23次。
-
在十位上,3出现在30-39,130-139,230-238,一共是20+9=29次,小于3的
是30次,而大于3的数,有20次。 -
在百位上,2出现39次,小于2的出现100次,大于2出现0次
-
统计8,一共出现24+20=44次。
不难发现,在每个位上时,都分成三部分,小于该数的和等于该数的和大于该数的。
-
当小于时,次数为(高位+1)*对应位次:如个位,238高位为23,低位为0,次数为(23+1)*1=24。十位238高位为2,次数为(2+1)*10=30次,百位(0+1)*100=100次
-
等于时,次数为高位对应位次+(低位+1):个位上,238高位为23,低位为0,次数为231+0+1=24。十位238高位为2,低位为8,次数为2*10+8+1=29次,百位(0)*100+38+1=39次
-
大于时,次数为高位对应位次:个位上,238高位为23,次数为231=23。十位238高位为2,次数为2*10次,百位(0)*100=0次
但是,0,要特殊处理,因为每一次都把0当作前导处理了
可以写代码了(鼓掌
#include<bits/stdc++.h>
using namespace std;
int k,a[10]={0,0,0,0,0,0,0,0,0,0};//计数器
void count(){
int len=to_string(k).length();//把k转成字符串再求其长度
for(int i=0;i<len;i++){
int num=(k/(int)pow(10,i))%10;//求当前数字
int high=k/(int)pow(10,i+1);//高位
int low=k%(int)pow(10,i);//低位
//相等时
a[num]+=high*pow(10,i)+low+1;
//小于时
for(int j=0;j<num;j++){
a[j]+=(high+1)*pow(10,i);
}
//大于时
for(int j=num+1;j<10;j++){
a[j]+=(high)*pow(10,i);
}
//0处理
a[0]-=(int)pow(10,i);
}
for(int j=0;j<10;j++){
cout<< a[j]<<endl;
}
}
int main(){
cin>>k;
count();
}
—————— The end——————