计数问题——统计给定页码之前0-9数字出现次数(java语言实现)

该博客介绍了如何使用数学归纳法找出规律,解决计数问题,特别是统计给定页码之前0-9数字出现次数。通过分析高位、中间位和最低位数字的影响,得出计算公式,并提供了Java实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6页用数字6表示,而不是06或006等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,...,9。

输入

只有1行,表示书的总页码的整数n。

输出

共有10行,在第k行输出页码中用到数字k-1的次数,k=1,2,...10。

样例输入

11

样例输出

1
4
1
1
1
1
1
1
1
1
方法:数学归纳法找出规律。

①     8

 

涉及数字

出现次数

个位

1-8

1

②     27

 

涉及数字

出现次数

个位

0-9

2

个位

1-7

1

十位

1

10

十位

2

8

③     462

 

涉及数字

出现次数

个位

0-9

46

个位

1-2

1

十位

0-9

40

十位

1-6

10

百位

1-3

100

百位

4

63

④     7891

 

涉及数字

出现次数

个位

0-9

789

个位

1-2

1

十位

0-9

780

十位

1-8

10

十位

9

2

百位

0-9

700

百位

1-7

100

百位

8

92

千位

1-6

1000

千位

7

892

核心思路:

将数字的每一位的计数分解成:高位对低位的影响计数和自身数值的影响计数。

其中,高位对低位影响表现形式是:低位上0-9数字组的出现次数;

低位自身的影响技术是:对高位取低位的模,得到的余数加一。


.例子分析

抽象数字为···xxAxx···,探究数字A所在的位数和其数值的联系。

1.      当数字A是最低位时,其出现次数与高位的数字有关。

如: 页码462中的2,其共出现

46次完整的0-9集体计数加一(num/10)

1次0-2上的计数加一

2.      当数字A是中间位时,其出现次数与高位及低位有关。

如:页码462中的6,其共出现

40次完整的0-9集体计数加一((num/100)*10

10次0-5上的计数加一

3次数字6的计数加一

3.      当数字A是最高位时,其出现次数与自身和低位有关。

如:页码462中的4,其共出现

100次1-3的计数加一

63次数字4的计数加一

二.发现规律:

将0-9视作一组数字单元,表示对0-9每个数字都计数加一。记为符号X。

设此时数字为m,位数n。

1.      对非最高位:

X 出现次数:(num%pow(10,n+1))*pow(10,n)

小于m的1-9中的数字出现次数:pow(10,n)

等于m的数字出现次数:num%pow(10,n)+1

2.      对最高位:

小于m的1-9中的数字出现次数:pow(10,n)

等于m的数字出现次数:num%pow(10,n)+1

其中pow(a,b)表示数字a的b次方。

三.对0的探究

1.      取模加一是为了得到余数并且考虑个位出现一次0的情况。

2.      页码是从1开始计数,且小数字前面不加0。

如:372页中,97页数字是97,而非097.

因此,对非最高位来说:操作

X 出现次数:(num%pow(10,n+1))*pow(10,n) 

已经将最高位为1、本位数字为0的情况考虑进去了,因此,需要在取模操作时将本位数字为0的情况减去。因此,对非最高位且本位数字小于m的数字的计数操作,都是从数字1开始计数,不再对0进行计数。

import java.util.Arrays;
import java.util.Scanner;

public class PageCount {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		sc.close();
		int[] count = solve(num);
		for (int i = 0; i <= 9; i++) {
			System.out.println(count[i]);
		}
	}

	public static int[] solve(int num) {
		int[] count = new int[10];
		Arrays.fill(count, 0);
                //value数组用于存储数字的每一位,由低位到高位存储。(123——>3,2,1)
                int[] value = trans(num);
                //max是给定数字的最高位
                int max = value.length;
		// 遍历数字的每一位
		for (int i = 0; i < max; i++) {
			// 对小于每一位上的数字值的数字进行计数
			for (int j = 1; j <= value[i]; j++) {
				// 对小于当前位的数字值计数次数为:10的当前位数的次方
				if (j < value[i])
					count[j] += Math.pow(10, i);
				// 对当前位的数字值对应的数字计数为:num对10的i次方求模,再加一
				else
					count[j] += (int) (num % Math.pow(10, i)) + 1;
			}
		}
		// 遍历数字的每一位,最高位不遍历
		for (int i = 0; i < max - 1; i++) {
			// 遍历0-9数字
			for (int j = 0; j <= 9; j++) {
				// 对count[]数组每一个元素进行计数
				count[j] += ((int) (num / Math.pow(10, i + 1)) * Math.pow(10, i));
			}
		}
		return count;
	}

        //trans函数将给定数字转换为int数组保存,且由低位到高位排列(数字123转换为数组value[],且值依次为3,2,1)
	public static int[] trans(int num) {
		String str = String.valueOf(num);
		char[] array = str.toCharArray();
		int[] value = new int[array.length];
		for (int i = 0; i < array.length; i++) {
			value[i] = (int) array[array.length - 1 - i] - (int) ('0');
		}
		return value;
	}

}

补充:
顺便贴上 C的暴力实现:
#include <iostream>
#include <cstring>
using namespace std;
int main(){
    long n,f[10],i,t;
    memset(f,0,sizeof(f));
    while(cin >> n){    	
    	for(i=1;i<=n;i++){
    		t=i;
    		while(t){
			    f[t%10]++;
			    t=t/10;
			}    		
		}		
		for(i=0;i<=9;i++){
			cout << f[i] << endl;
		} 		  	
	}        
    return 0;
}
C++分块实现
#include <iostream>
#include <cstring>
using namespace std;
long f[10],i,l,h;
char s[10]; 
long a[10] = {0, 1, 20, 300, 4000, 50000, 600000, 7000000, 80000000, 900000000};
long b[10] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
void compute(int n){	
    sprintf(s,"%d",n);
	l = strlen (s);	
	if(l==1)	
	{
		for(i=1;i<=n;i++)
    		f[i]=f[i]+1;
    	return;
	}
	else
	{
		h=s[0]-48;
		for(i=0;i<=9;i++){
			f[i]=f[i]+a[l-1];			
		}
		
		f[0]=f[0]-b[l-1];
		
		for(i=0;i<=9;i++){
			f[i]=f[i]+(h-1)*a[l-1];			
		}
				
		for(i=1;i<h;i++){
			f[i]=f[i]+b[l];
		}
		
		f[h]=f[h]+n-h*b[l]+1;
		n=n-h*b[l];
		f[0]=f[0]+b[l-1];
		compute(n);
	}	
}
int main()
{
	int n;    
    cin >> n;
	compute(n);    
	for(i=0;i<=9;i++)
	{
	    cout << f[i] << endl;
	} 				  	     
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值