从1到n整数中1出现的次数

题目描述:

输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含“1”的数字有1,10,11和12,其中“1”一共出现了5次。

样例:

输入: 13
输出: 6
解释: 数字 1 出现在以下数字中: 1, 10, 11, 12, 13 。

分析:

首先先看数字n是几位数,记为len。
第二步取出n最高位的数字,判断该数字和1的关系,计算最高位1出现的次数:

  • 如果最高位数字大于1,那么最高位数字出现数字1的次数为10的len-1次方。(如n=21345,则1在最高位出现的次数为10000)
  • 如果最高位数字等于1,那么最高位数字出现数字1的次数为除去最高位,其余为组成的数字+1。(如n=11345,则1在最高位出现的次数为1346)
  • 如果最高位数字小于1,那么最高位数字出现数字1的次数为0。

第三步判断其余位1出现的次数,其余位的次数要受到高位和低位的影响:

  • 首先取出当前位的数字cur,判断和1的大小关系,取出高位的数字befor;
  • 如果第i位数字大于1,那么第i位数字出现1的次数等于(最高位至第i+1位数字+1)乘以10的i-1次方
  • 如果第i位数字小于1,那么第i位数字出现1的次数等于(最高位至第i+1位数字)乘以10的i-1次方
  • 如果第i位数字等于1,那么第i位数字出现1的次数等于(最高位至第i+1位数字)乘以10的i-1次方+(第i-1位一直到最后一位的数字+1)
  • 比如n=2345,len=4,计算十位数字上1出现的次数,也就是第i=2位,首先取出十位上的数字cur=4大于1,则该位1出现的次数等于(23+1)*10的i-1次方=240.(10-19,110-119,210-219,…,910-919,1010-1019,1110-1119,…,1910-1919,2010-2019,2110-2119,2210-2219,2310-2319 )
  • 比如n=2305,len=4,计算十位数字上1出现的次数,也就是第i=2位,首先取出十位上的数字cur=0小于1,则该位1出现的次数等于(23)*10的i-1次方=230
    (10-19,110-119,210-219,…,910-919,1010-1019,1110-1119,…,1910-1919,2010-2019,2110-2119,2210-2219)
  • 比如n=2315,len=4,计算十位数字上1出现的次数,也就是第i=2位,首先取出十位上的数字cur=1等于1,则该位1出现的次数等于(23)*10的i-1次方=230加上5+1等于236。(10-19,110-119,210-219,…,910-919,1010-1019,1110-1119,…,1910-1919,2010-2019,2110-2119,2210-2219,2310-2315)同时也等于(23+1)*10的i-1次方-((231+1)*10的i-1次方-n-1)
//执行用时 : 0 ms, 在Number of Digit One的Java提交中击败了100.00% 的用户
//内存消耗 : 32.5 MB, 在Number of Digit One的Java提交中击败了44.19% 的用户
	public int countDigitOne(int n) {
		if (n < 1)
               return 0;
		int len = getLenOfNum(n);//获得n是几位数
		if (len == 1)
			return 1;
		int tmp = (int) Math.pow(10, len - 1);
		int sum;
		//n/tmp代表最高位的数字
		/*
		 * 如果最高位数字大于1,那么最高位数字出现数字1的次数为10的len-1次方
		 * 如果最高位数字等于1,那么最高位数字出现数字1的次数为除去最高位,其余为组成的数字+1
		 * 如果最高位数字小于1,那么最高位数字出现数字1的次数为0
		 */
		int high=n/tmp;
		if (high > 1) 
			sum = tmp;
		else if (high < 1) 
			sum = 0;
		else 
			//n%tmp表示当前位的低位数字
			sum = n % tmp + 1;
		//如果len=5,最高位代表是第五位
		//获取其余几位出现数字1的次数
		for (int i = len - 1; i > 0; i--) {
			int x = n / (int) Math.pow(10, i);// 获取高位数字
			int z = n / (int) Math.pow(10, i - 1);//高位和当前位的数字 
			int y = z % 10;//获取当前位数字
			//求第i位数字上1出现的次数    
			/*
			 * 如果第i位数字大于1,那么第i位数字出现1的次数等于(高位数字+1)乘以10的i-1次方
			 * 如果第i位数字小于1,那么第i位数字出现1的次数等于(高位数字)乘以10的i-1次方
			 * 如果第i位数字等于1,那么第i位数字出现1的次数等于(高位数字)乘以10的i-1次方+(低位+1)
			 * 同时也等于(高位数字+1)乘以10的i-1次方-((高位和当前位的数字 +1)乘以10的i-1次方-n-1)
			 * 所以中间位数1出现的次数要受到高位数字和低位数字的影响
			 */
			int pown=(int) Math.pow(10, i - 1);
			if (y > 1) 
				sum = sum + (x + 1)*pown;
			else if (y < 1) 
				sum = sum + x * pown;
			else 
				sum = sum + (x + 1)*pown - ((z + 1)*pown - n - 1);
		}
		return sum;
    }
	private int getLenOfNum(int n) {
		int len = 0;
		while (n != 0) {
			len++;
			n /= 10;
		}
		return len;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值