1. 程式人生 > >計數問題——統計給定頁碼之前0-9數字出現次數(java語言實現)

計數問題——統計給定頁碼之前0-9數字出現次數(java語言實現)

題目描述

一本書的頁碼從自然數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;
}