统计数字问题

【座右铭】1. 想要成为行家,就必须尝试解决大量的问题;

                    2. 解决大量问题并不代表能解决所有问题,而是表示解决下一个问题的几率变大了

1.一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导0。例如第6页,而不是06页,或者006页。要求给定的页码n,统计出全部页码中出现了多少次0,1,2,...,9

第一部分:思路

1. 对于数字n=a1a2a3...ak,假设页码包含前导0,从左到右统计,统计方法如下

1.1 将a1a2a3...ak分为如下部分:

1) 从000..0(k个0)到 (a1-1)99...9之间0-9出现的次数

2) 再将a1的次数加上:a2a3...ak + 1次

3) 再递归统计n=a2a3...ak中0-9出现的次数

举例说明:n=263

1) 先统计000-199之间0-9出现的次数

2) 再将2的次数加上:63+1=64次

3) 再递归统计n=63时0-9出现的次数


1.2 统计000..0(k个0)到(a1-1)99...9之间0-9出现的次数如下:

1) 最高位0,1,2...(a1-1)都出现了10^(k-1)次

2) 在余下的k-1位中,出现了a1次从00..0(k-1个)到99..9(k-1个)。因为0-9出现的次数相等,因此0-9分别出现了 a1*(k-1)*10^(k-1)/10

举例说明:从000-199

1) 最高位0和1都出现了100次

2) 然后00-99出现了2次,因此0-9分别出现了2*2*100/10=40次


2.减去前导0,k位数中前导0的数目如下:

1个数:全是0,如00...00。推出k个前导0

9个数:非0数从倒数第1位开始,如00...0X。推出(k-1)*9个前导0

90个数:非0数从倒数2位开始,如00...0XX。推出(k-2)*90个前导0

900个数:非0数从倒数第3位开始,如00...0XXX。推出(k-3)*900个前导0

依次轮推

第二部分:Java代码,不考虑异常

/**
	 * 一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导0。
	 * 例如第6页,而不是06页,或者006页。要求给定的页码n,统计出全部页码中出现了多少次0,1,2,...,9
	 * @param page
	 * 页码
	 * @return
	 * 一个长度为10的数组,分别表示0,1,2...9出现的次数
	 */
	public static int[] statistic(int page)
	{
		//数字不超过20位
		final int integerLen = 20;
		//保存数位
		int[] dbit = new int[integerLen];
		int len = 0;
		while(page>0)
		{
			dbit[len++] = page%10;
			page /= 10;
		}
		//用于步骤1.1中的第2步
		int[] dnum = new int[integerLen];
		dnum[0] = 0;
		int base = 1, k = 1;
		for(;k<len;k++)
		{
			dnum[k] = dbit[k-1]*base + dnum[k-1];
			base *= 10;
		}
		k = 0;
		for(;k<len;k++)
		{
			dnum[k]++;
		}
		
		//步骤1
		int[] result = new int[10];
		k = len;
		base = 1;
		while((--k)>0)
		{
			base *= 10;
		}
		k = len;
		while((--k)>=0)
		{
			int curbit = dbit[k];
			//步骤1.2中第1步
			int j = 0;
			for(;j<curbit;j++)
			{
				result[j] += base;
			}
			
			//步骤1.1中第2步
			result[j] += dnum[k];
			
			//步骤1.2中第2步
			base /= 10;
			j = 0;
			int add = base*k*curbit;
			for(;j<10;j++)
			{
				result[j] += add;
			}
		}
		
		//步骤2:减去前导0
		k = 1;
		base = 9;
		result[0] -= len;
		for(;k<len;k++)
		{
			result[0] -= (len-k)*base;
			base *= 10;
		}
		
		return result;
	}
	
	/*
	 * 蛮力法:用于调试
	 */
	static int[] statistic2(int page)
	{
		int[] result = new int[10];
		for(int i=1;i<=page;i++)
		{
			int j = i;
			while(j>0)
			{
				result[j%10]++;
				j /= 10;
			}
		}
		return result;
	}

第三部分:测试用例

n=1,0到9出现的次数:0 1 0 0 0 0 0 0 0 0 

n=11,0到9出现的次数:1 4 1 1 1 1 1 1 1 1 

n=100,0到9出现的次数:11 21 20 20 20 20 20 20 20 20

n=2001,0到9出现的次数:494 1601 602 600 600 600 600 600 600 600 

n=67321,到9出现的次数:26162 37273 37264 37184 37162 37162 34484 26484 26162 26162

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值