问题描述
一本书的页码从自然数1开始顺序编码直到自然数n。输的页码按照通常的习惯编排,每个页码都不含有多余的前导数字0。例如,第6页用数字6表示,而不是06或者006等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2…8,9。
算法设计:给定表示书的总页码的十进制整数n(1<=n<=10^9),计算书的全部页码中分别用到多少次数字0,1,2…8,9。
数据输入:输入数据由文件名为input.txt的文本提供。每个文件只有1行,给出表示书的总页码的整数n。
结果输出:将计算结果输出到文件output.txt。输出文件总共10行,在第k行输出页码中用到数字k-1的次数,k=1,2,…,10。
题解
方法1(暴力求解)
遍历每一个数字的每位数,对应数字出现的次数+1
代码
public static void count_one(File in, File out) throws FileNotFoundException {
Scanner sc = new Scanner(in);
int n=sc.nextInt(); //书的总页码
int[] arr = new int[10];
int i,j,m;
for (i=1;i<=n;i++){
if (i<10){
arr[i]++;
}
if (i>=10){
j=i;
while (j!=0){
m=j%10; //求余
arr[m]++;
j=j/10;
}
}
}
PrintWriter p = new PrintWriter(out);
for (i=0;i<10;i++){
p.println(arr[i]);
}
p.close();
sc.close();
}
方法2(补0+部分暴力求解)
补0
n位数字,共有10n种可能(0…0也算),那么0-9每一个数字都出现了n*10n-1次。
0-9 无效的0个数:1
00-99 无效的0的个数:10+1(00-09)共有11个0无效
000-999 无效的0的个数:100+10+1
由此,我们可以发现无效的0个数f(n)
分区
将数字n分为几个区,例如将3825分为[000, 999], [1000, 1999], [2000, 2999]三部分。
先忽略后两个分区的最高位,那相当于三个[000,999],那0-9各出现了 3 * 3 * 10(3-1) 次。
这三个分区中只有第一个分区才有无效的0,个数为f(length-1)
长度为length的数字n有这样的分区数p=n/(10length-1)
统计最高位
最高位分别为1,…, p,各自出现了10length-1次
剩余部分[3000, 3825]使用暴力求解的方法,即[p*10(length-1), n]
代码
//补0+部分暴力求解
public static void count_two(File in, File out) throws FileNotFoundException {
Scanner sc = new Scanner(in);
int n = sc.nextInt();
String s = String.valueOf(n);
int length = s.length();
int[] arr = new int[10];
int p = area_num(n,length);//块数
for (int i=0;i<10;i++){
arr[i]+=p*fn(length-1);
}
//处理分区最高位
if (p>1){
for (int i=1;i<p;i++){
arr[i]+=power(10,length-1);
}
}
int zero = zero_num(length-1);
arr[0]-=zero;
//处理剩余的数
int r = p*power(10,length-1);
int i,j,m;
for (i=r;i<=n;i++){
if (i<10){
arr[i]++;
}
if (i>=10){
j=i;
while (j!=0){
m=j%10; //求余
arr[m]++;
j=j/10;
}
}
}
PrintWriter pw = new PrintWriter(out);
for (i=0;i<10;i++){
pw.println(arr[i]);
}
pw.close();
sc.close();
}
//求单块分区0-9出现的次数
public static int fn(int n){
return n*power(10,n-1);
}
//求分区块数
public static int area_num(int n,int length){
return n/(power(10,length-1));
}
//求无效0的个数
public static int zero_num(int length){
if (length==1){
return 1;
}
int num = power(10,length-1)+zero_num(length-1);
return num;
}
//求a^b
public static int power(int a, int b) {
return (int) Math.pow(a, b);
}
运算速度对比
总结
网上还有O(logn)的方法,我没完全看懂,在它基础上加上暴力求解,速度比它慢,但比暴力求解要快。