输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12 输出:5
示例 2:
输入:n = 13 输出:6
限制:
1 <= n < 2^31
注意:本题与主站 233 题相同:https://leetcode-cn.com/problems/number-of-digit-one/
Related Topics
- 递归
- 数学
- 动态规划
著书三年倦写字,如今翻书不识志,若知倦书悔前程 ,无如渔樵未识时
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12 输出:5
示例 2:
输入:n = 13 输出:6
限制:
1 <= n < 2^31注意:本题与主站 233 题相同:https://leetcode-cn.com/problems/number-of-digit-one/
按位计算统计,(【左侧数值】当前位1【右侧数值】)
直接拿一个数字31261分析下,过程如下
3 1 2 6 1
3126 + 1
3130
3200
3000 + 261 + 1
10000
1,这个位置上1粗线的次数由他前面的数字决定,所以我们可以非常明白的知道,这个时候这一位一共出现了3127次字符1,n = 1的时候的1次6,这个数字大于1,根据上面算最低位的时候的情况分析依据,我们可以知道这个位置一共出现了3130次,这个结果可以也可以分为两部分xxx1x的情况,当前位前面有312种情况,而又因为当前位大于1,后面一位的0-9的10种情况,即为3120种10到19的10种情况( 312 + 1 ) * 10 种。2,同前面的数字6的情况,因为大于1所以结果为(31 + 1) * 100种1,虽然前面为1的情况我们已经分析过了,但是那个是不完整的,在这里,我们将完整分析下为1的时候的情况31xxx区段是还没完全结束的,所以我们不能直接得到结果为40000,1,2的3000种情况,也可以认为如果左侧数字是k,那么现在就有k000次31xxx区段是还没完全结束,所以应当还有当前位后面部分的261种情况,外加如果后面都为0的情况( 0 + 1 ) * 100001xxx,11xxx,21xxx的3000种情况我们在计算中使用了一个辅助变量10,100,1000,10000,这个数值是比入参数字大一位的
又因为题目给的入参条件1 <= n < 2^31
所以中间计算过程我就偷了个懒给转成了long型数据
另外,葱低往高取每一位的数字的时候可以直接用余10,之后再除10的方法,我这边一开始是准备转成数组直接根据数组计算左右侧数据影响的当前位置1出现次数的,最后就还是没改了
class Solution {
/*
3 1 2 6 1
3127
3130
3200
3000 + 261 + 1
10000
*/
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
public int countDigitOne(int n) {
long ans = 0;
long num = (long) n;
int size = numSize(n);
int[] arr = new int[size];
int idx = size-1;
while (n != 0){
arr[idx] = n%10;
n /= 10;
idx--;
}
idx = size-1;
long tmp = 10;
while (idx >= 0){
long r = 0;
if (arr[idx] == 0){
r = ( num / tmp ) * ( tmp / 10 );
}
if (arr[idx] == 1){
r = ( num / tmp ) * ( tmp / 10 ) + ( num % (tmp / 10) ) + 1;
}
if (arr[idx] > 1){
r = ( (num / tmp)+1 ) * ( tmp / 10 );
}
ans += r;
tmp *= 10;
idx--;
}
return (int)ans;
}
static int numSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
}
发表评论