题目描述:
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
示例:
输入: 13
输出: 6
解释: 数字 1 出现在以下数字中: 1, 10, 11, 12, 13 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-digit-one
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
剑指offer上的思路有点复杂,没看懂…
思路:
看看这个思路
添加链接描述
此题可以分解为分别计算各位上出现1的次数的和,那么各位上出现1的次数应该如何计算呢
我们拿2104举例,在我们分别计算各位1出现的次数时,我们将数字分为preNum,curNum,sufNum。
1.计算千位出现1的次数:
preNumber = 0
curNumber = 2
sufNumber = 104
此时curNumber > 1,那么当千位为1时,即1****,可能出现1的次数有1000次(1000->1999)。此时preNumber = 0,不能借位所以只有这一种情况出现。即千位出现1的次数为1000
2.计算百位出现1的次数:
preNumber = 2
curNumber = 1
sufNumber = 4
此时curNumber = 1,那么当百位为1时,即1**,可能出现是次数又是多少呢?
我们发现现在preNumber = 2,此时百位可向前面借位
那么在不借位的情况下即21**可能会出现的情况为(2100->2104),次数就是sufNumber + 1 即4 + 1 = 5
当向前面借位时就可能出现的情况为(*100->*199),我们发现每借位一次就有100中情况,总共有preNumber * 100中情况
不借位加上借位的情况出现的次数就是(4 + 1)+ 2 * 100 = 205
3.计算十位出现1的次数
preNumber = 21
currentNumber = 0
sufNumber = 4
此时curNumber < 1,若此时十位要出现1,肯定需要向前面借位,每次借位出现1的情况为(**10->**19),也就是10
总共出现的1情况就是,preNumber * 10 = 21 * 10 = 210
4.计算个位出现1的次数
preNumber = 210
curNumber = 4
sufNumber = 0
此时curNumber又是>1,而且可向前借位,在不借位的情况下,各位1出现的次数为(2101)只有一次,
在借位的情况下为(***1)只有一种,可借位210次
总共出现1的情况为1 + 210 * 1 = 211
全部位加起来为1000 + 205 + 210 + 211 = 1626
可总结出公式为:
num为位数,即千位时num = 1000
sum为当前位数出现1的次数
当curNumber > 1 时:
sum = num + num * preNumber
当curNumber = 1 时:
sum = sufNumber + 1 + num * preNumber
当curNumber < 1 时:
sum = num * preNumber
class Solution {
public int countDigitOne(int n) {
if(n < 0){
return 0;
}
String string = Integer.toString(n);
// 从高位到低位依次进行
int len = string.length() - 1;
int result = 0;
for (int i = 0; i < string.length(); i++) {
String tem = string.substring(0,i);
int pre = 0;
int cur = Integer.valueOf(string.substring(i,i + 1));
int last = 0;
String substring = string.substring(i + 1);
if(substring.length() != 0){
last = Integer.valueOf(substring);
}
if(tem.length() != 0){
pre = Integer.valueOf(tem);
}
result += (pre) * Math.pow(10,len - i);
if(cur > 1){
result += Math.pow(10,len - i);
}else if(cur == 1){
result += last;
result ++;
}
}
return result;
}
}