题意
输入一个整数 n
,求1~n 这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12 这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
解题思路
将 1 ~ n 的个位、十位、百位、...的 1 出现次数相加,即为 1 出现的总次数。
例如:n=3101592,那么我们要求的就是从3101592、3101591 .... 1,这些所有数字中,1在个位数字上出现的次数+1在十位上出现的次数+.....。
比如我们现在要求1在百位上出现的次数,首先我们定义一个变量 base=100,然后让当前在百位上的数字5记做cur,而在百位之前的数字记为high,百位之后的数字记作low。
我们发现,high=n/base/10;low=n%base;cur=(n/base)%10。
假设我们将百位上的数字cur定位1,那么它出现的次数等于 在它前面可能出现的数字的个数*在它后面可能出现数字的个数。
在本例中,在cur前面出现数字的范围为:0000~3101,而在它后面的数字变化范围是 00~99。
我们发现cur前面部分的可能出现数字的个数恰好就是 high+1,而在它后面可能出现的数字的个数恰好就是(99-0+1)=100,即base。
因此,1在百位上出现的次数=(high+1)*base。
推论:
n=3101592。
- 若我们求的是1在千位上出现的次数,此时cur=1,而它前后出现的数字的个数情况有两种。
- high从000~309,low变化范围就是000~999
- high取310,low部分的变化范围就只能是000~592,因为此时如果low部分超过了592,整个的数字值就大于了给定的数字值。
- 因此在第一种情况下,cur前面部分可能的取值范围是(309-0+1=310),即high;而cur后面部分的取值范围是(999-0+1=1000),即base。
- 第二种情况下,cur前面部分部分出现的次数为1,后面部分取值范围是(592-0+1=593),即low+1。
- 所以,我们计算1在千位上出现的次数=(high*base)+(1*(low+1))
- 求1在万位上出现的次数:
- 此时,cur=0。
- cur前面部分可能出现的数字范围为:00~30,次数为(30-0+1),即high。
- cur后面部分可能出现的数字范围为:0000~9999,次数为(9999-0+1),即base。
- 所以,1在万位上出现的次数为:high*base。
总结:
- cur>1,我们返回的结果是:(high+1)*base
- cur==1,我们返回的结果:high*base+low+1
- cur==0,我们返回的结果是:high*base
C++实现
class Solution
{
public:
int countDigitOne(int n)
{
//从个位开始,以此计算1在个位、十位、百位上出现的次数
long base=1; //bae初始化为1,代表着从个位数上开始计算
int res=0; //最终的结果
while(base<=n)
{
//计算cur之后的部分的数字low
int low = n%base;
//计算cur
int high = n/base;
int cur = high%10;
//计算high
high=high/10;
if(cur>1)
{
res+=(high+1)*base;
}
else if(cur==1)
{
res+=(high*base+low+1);
}
else //cur小于1
{
res+=high*base;
}
base*=10; //更新base,计算上一个位数上1出现的次数
}
return res;
}
};