这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
233. 数字 1 的个数
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
示例 1:
输入:n = 13 输出:6 示例 2:
输入:n = 0 输出:0
解题思路
根据题目要求,我们需要统计 [0,n] 范围内所有整数中,数字 1 出现的个数。由于 n 的范围最大为 10^9 ,它是一个 10 位整数,因此我们可以考虑枚举每一个数位,分别统计该数位上数字 1 出现的次数,最后将所有数位统计出的次数进行累加即可得到答案
bef代表当前位前面的数字大小
w代表当前位的权重,例如10,100,1000
n代表当前位后面的数字大小
if (当前位>1)
{
对答案贡献:(n+1)*w;
}else if (当前位==1)
{
对答案贡献:n*w+bef+1;
}else 对答案贡献:n*w;
正确性证明
例如:对于n=3015, 我们先想象有一个自行车密码锁(这个比喻来自@ryan0414),一共有四位,每一位可单独滚动。为了计算十位出现1的次数,我们考虑三种情况:
- 固定个位1,个位为1的数字组成的可能是[000-301]1,所以共有302个数字个位数为1,所以对答案贡献302个1。
- 固定十位1,十位为1的数字组成为[00-29]1[0-9]和[30]1[0-5],所以共有30 * 10+6个数字个位数为1,所以对答案贡献306个1。
- 百位为1的数字组成为[0-2]1[00-99],所以共有300个数字个位数为1,所以对答案贡献200个1。
- 千位数字为1很明显只有1000个数字,1[000-999],所以对答案贡献1000个1。
所以最后的总和为1908
代码
class Solution {
public int countDigitOne(int n) {
int bef=0,res=0,w=1;
while (n>0){
int cur=n%10;
n/=10;
if (cur>1)
{
res+=(n+1)*w;
}else if (cur==1)
{
res+=n*w+bef+1;
}else res+=n*w;
bef+=cur*w;
w*=10;
}
return res;
}
}