题目描述:
输入一个整数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;
}