统计数字问题(10^9的大数)

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

数据输入:每个文件只有一行,给出表示书的总页码的整数n(1<=)

数据输出:输出文件共10行,在第k(k=1,2,3...10)行输出页码中用到数字k-1的次数

输入文件示例:11

输出文件示例:1 4 1 1 1 1 1 1 1 1

 问题分析:

拿到题第一感觉是直接从1循环到n,然后计算每一位的数字计算总数

for(int i=1;i<=n;i++){
	while(i/10){
		total[i%10]++;
		i/=10;
	}
}

可以看到,随着n的增大,程序的时间复杂度也在增大,所以方法不可取

然后我就用老师上课讲的方法试说一下吧

算法核心思想是考虑数字k在每个位上出现的次数然后加和

然后现在来找规律

拿9527举例吧,现在十位上是2,假设考虑十位上的数比2大,例如3,则十位上出现3的数字为

30-39  130-139....9430-9439 共(94+1)*10个

假设考虑十位上的数比2小,例如1,则十位上出现数字2的数字为

10-19  110-119....9510-9519 共(95+1)*10个

假设考虑十位上的数等于2,则十位上出现2的数字为

20-29 120-129....9420-9429 9520-9527 共(94+1)*10+(个位+1)

 

再用百位上的5举例,现在百位上是5,假设考虑百位上的数比5大,例如6,则百位上出现6的数字为

600-699  1600-1699....8600-8699 共(8+1)*100个

假设考虑百位上的数比5小,例如3,则百位上出现数字3的数字为

300-399  1300-1399....9300-9399 共(9+1)*100个

假设考虑百位上的数等于5,则百位上出现5的数字为

500-599 1500-1599....8500-8599 9500-9527 共(8+1)*100+(后两位+1)

 

然后假设当前位为cur,该位之前的数字为high,该位之后的数字为low,cur所在的位的权重为weight(十位为10,百位为100),则可以总结出的规律为:

        k < cur时,k在该位出现的次数为(high+1)*weight

        k > cur时,k在该位出现的次数为 high*weight

        k = cur时,k在该位出现的次数为 high*weight+low+1

Note:

因为0前必须有数字,所以需要单独考虑0

看代码吧还是

#include<iostream>
#include<cstring>
using namespace std;

__int64 total[10];//记录每个数出现的个数 

int main(){
	int n;
	__int64 low,cur,high;
	int weight=1;//权值 
	cout<<"Enter the total page number n:";
	cin>>n;
	memset(total,0,sizeof(total));
	
	while(n/weight){//将0单独考虑 
		low=n%weight;//当前位之后的数字 
		cur=(n/weight)%10;//当前位 
		high=n/(weight*10);//当前位之前的数字
		if(!high){
			weight*=10;
			continue;
		}
		else if(cur==0){
			total[0]+=((high-1)*weight+low+1);
		}
		else{
			total[0]+=high*weight;
		}
		weight*=10; 
	}
	
	weight=1;
	while(n/weight){//1~9直接按照规律
		low=n%weight;//当前位之后的数字 
		cur=(n/weight)%10;//当前位 
		high=n/(weight*10);//当前位之前的数字 
		for(int i=1;i<=9;i++){ 
			if(i<cur){
				total[i]+=(high+1)*weight;
			}
			else if(i>cur){
				total[i]+=(high*weight);
			}
			else if(i==cur){
				total[i]+=(high*weight+low+1);
			}
		}
		weight*=10;
	}
	
	for(int i=0;i<=9;i++){
		cout<<total[i]<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值