传送门: 数字1的个数
题意
给定数字n,求所有不大于数字n的正整数中,出现的1的个数
举例9527
从最数位开始依次往低位遍历,则遍历顺序为:9 -> 5 -> 2 -> 7
数位9
首先不考虑本数位上的1,而考虑比本数位低位的数位上的1。
在该数位上,若我们这个数位上的数字为0-8的数字,则其他数位的数字可以任意选择,使得整体的数字不会超过9527.
若确定本数位为0-8,则此时剩下3个数位未确定,在这3个数位上,我们可以这样安排数字,使得其出现1:(1* *) 或 (* 1 *) 或 (* * 1)
其中 * 代表可以用任何数字替换,不难得出3个数位中,任意组合的所有数字中1的个数一共是 3*100= 300 个,而0-8一共有9个数字,所以此时答案需要加上9*300 = 2700个1。
其次考虑本数位上的1。
因为0-8中可以选择1,考虑选择1的情况,则可知一共有1000个数字(1000-1999)包含1,所以此时答案需加上1000个1。
至此 0000-8999 我们已经考虑完毕
数位5
接下来考虑 9000-9499
在该数位上,若我们这个数位上的数字为0-4的数字,则其他数位的数字可以任意选择,使得整体的数字不会超过9527.
若确定本数位为0-4,则此时剩下2个数位未确定,在这2个数位上,我们可以这样安排数字,使得其出现1:(1 *) 或 (* 1)。
不难得出2个数位任意组合的所有数字中的1的个数一共是2*10 = 20个,而0-4一共有5个数字,所以此时答案需要加上5*20 = 100个1。
因为0-4中可以选择1,考虑选择1的情况,则可知一共有100个数字(9100-9199)包含1,所以此时答案需加上100个1。
至此 9000-9499 我们已经考虑完毕
数位2
接下来考虑 9500-9519
在该数位上,若我们这个数位上的数字为0-1的数字,则其他数位的数字可以任意选择,使得整体的数字不会超过9527.
若确定本数位为0-2,则此时剩下1个数位未确定,在这1个数位上,我们可以这样安排数字,使得其出现1:(1 )。
不难得出2个数位任意组合的所有数字中的1的个数一共是1*1 = 1个,而0-1一共有2个数字,所以此时答案需要加上2*1 = 2个1。
因为0-1中可以选择1,考虑选择1的情况,则可知一共有10个数字(9510-9519)包含1,所以此时答案需加上10个1。
至此 9500-9519 我们已经考虑完毕
数位7
接下来考虑 9520-9527
在该数位上,若我们这个数位上的数字为0-7的数字,因为是最后一位了,仅能选择1。答案加上1。
至此 9520-9527 我们已经考虑完毕
因此答案是 2700 + 1000 + 100 + 100 + 2 + 10 + 1 = 3913
注意
举例 1798
则数位遍历: 1 -> 7 -> 9 -> 8
当遍历至 1 时,由于选择1时不能任意改变低位的数字,比如 1999 就不行,所以考虑本数位的1而进行的计算,应当加上剩下的低位数字表示的最大数字+1,即798+1 = 799。为什么要+1呢?因为1000需要被考虑在内。
举例 1078
数位遍历至0时,不用进行任何操作。
答案
class Solution {
typedef long long LL;
public:
int countDigitOne(int n) {
//n为1时,big为1
//n为985时,big为100
//n为100时,big为100
//n为3567时,big为1000,
//以此类推
//big是用来从高位遍历至低位的
LL big = 1;
//len是big的总位数,ans是答案
int len = 1,ans = 0;
//分别代表了不同个数位个数能组合出的所有数字包含的1的个数
int cal[11]={0,1,20,300,4000,50000,600000,7000000,80000000,900000000,1000000000};
//计算big和len
while(big< n){
big*=10;
++len;
}
//比方说n = 100,上面的代码计算后big = 100,此时不用除以10
if(big!=n){
big/=10;
--len;
}
//遍历未完毕
while(n){
//now是当前位的最大数字
int now = n/big;
//如果now不为0
if(now){
//不考虑本数位上的1 :本数位可为0,1,2 ... now-1
ans+=now*cal[len-1];
//考虑本数位上的1 :
//若本数位大于1,则本位取1,低位可以任意取
//若本数位等于1,则只能取低位拼凑成的最大数字
ans+=now>1?big:n%big+1;
}
//去掉最高位
n%=big;
//相应地进行缩小
--len;
big/=10;
}
return ans;
}
};