1.问题描述
一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6页用数字6表示,而不是06或006等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。
2.算法设计
给定表示书的总页码的10进制整数n(1≤n≤109)。编程计算数的全部页码中分别用到多少次数字0,1,2,…,9。
3.数据输入
输入数据由文件名为input.txt的文本文件提供,每个文件只有1行,给出表示书的总页码的整数n。
4.结果输出
将计算结果输出到文件output.txt。输出文件共有10行,在第k行输出页码中用到数字k-1的次数,k=1,2,3,…,10。
5.求解问题的算法描述
{ 在一个n位十进制数的由低到高的第i位上,总是连续出现10^i个0, 10^i个1, ……, 10^i个9, 9之后又是连续的10^i个0, 10^i个1, ……, 10^i个9,这样循环出现。由此可以在第i位上求出每个数字出现的次数,而在第i个数位上,最前面的10^i个0是前导0,应该在统计的时候减掉。}
以上是我百度到的一段算法解析,没看太明白……
以下是我的分析:
我把页码看成是一个字符串,也就是 一个N位数 PageNum = AB…Z(其中0≤ A,B…,Z ≤9 ),比如A=a, B=b, …, C=c(0≤ a,b, … , z ≤9)
0.在A位置上:
number < a : count[number] += 10^(N-1);
number = a : count[number] += bc…z + 1;
number > a: count[number] += 0;
1.在B位置上:
number < b: count[number] += 10^(N-2) * (a+1);
number = b: count[number] += 10^(N-2) * a + (cd…z + 1);
number > b: count[number] += 10^(N-2) * a
2.在C位置上:
number < c: count[number] += 10^(N-3) * (ab+1);
number = c: count[number] += 10^(N-3) * (de…z + 1);
number > c: count[number] += 10^(N-3) * ab;
……
i.在I位置上:
number < i: count[number] += 10^(N-i-1) * (i前面的数字 + 1);
number = i: count[number] += 10^(N-i-1) * (i后面的数字 + 1);
number > i: count[number] += 10^(N-i-1) * i前面的数字;
PS:以上计算的时候并没有讨论0的情况,而是把0006中0出现的次数一起统计了进去,因此在最后需要去掉前导0的计数!
(思考这题的时候我其实是想从低位开始统计的,这样就可以不用考虑前导0 的问题,但是思考到一半还是换到了从高位开始,我想从低位开始找规律应该也是可行的,有空的时候我再从低位开始找规律的分析下)
(这个过程就像是小学时候的做奥数题,找规律一样~ 奥数题的话只要能找到规律算出答案就行了,但是算法呢还需要考虑得算法复杂度等,虽然不知道我写的算法算不算效率高的, 至少没有去一个一个数字遍历~)
6. 算法实现的关键技巧
在算法中要尤其注意每次循环中对最高位的计算,以及最后要减去前导0的个数。
7.实验分析及结论
public static void main(String[] args) {
int page = 0;
Scanner input;
Formatter output;
//读取页码数
try {
input = new Scanner(Paths.get("input.txt"));
if(input.hasNext()){
page = input.nextInt();
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
//统计数字
int count[] = count(page);
//写入文件
try {
output = new Formatter("output.txt");
for(int num : count)
output.format("%d%n", num);
output.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static int[] count(int page){
//初始化计数数组
int count[] = new int[10];
for(int i = 0; i < 10; i++){
count[i] = 0;
}
//得出页码数的位/长度
String str = Integer.toString(page);
int len = str.length();
int pre = 0;//前面最高位前面的数字
int now = 0;//当前的最高位
int rem = 0;//当前最高位后面的数字
for(int i = 0; i < len; i++){
rem = page % (int)(Math.pow(10.0, len-i-1));
now = page / (int)(Math.pow(10.0, len-i-1));
//计算小于当前最高位的数字在这个位置出现的次数
for(int j = 0; j < now; j++)
count[j] += Math.pow(10.0, len-i-1) * (pre + 1);
//计算当前最高位的数字在这个位置出现的次数
count[now] += Math.pow(10.0, len-i-1) * pre + rem + 1;
//计算大于当前最高位的数字在这个位置出现的次数
for(int j = now + 1; j <= 9; j++)
count[j] += Math.pow(10.0, len-i-1) * pre;
//将当前最高位加到前面的数字中
pre = pre * 10 + now;
//去掉当前最高位得到的数,也就是前面的余数
page = rem;
}
//去掉对前导0的计数
for(int i = 0; i < len; i++){
count[0] -= Math.pow(10.0, len-i-1);
}
return count;
}
以上就是我的第一题算法题~
希望自己可以坚持下去 :)