LeetCode 233.数字1的个数

题意

给定一个正整数 n n n,计算 [ 1 , n ] [1,n] [1,n]中所有整数出现的 1 1 1的个数之和。

题解

n n n的数字长度为 w w w n = d w d w − 1 . . . d 1 . n=d_wd_{w-1}...d_1. n=dwdw1...d1.考虑依次对这 w w w位进行计数。若现在正考虑从右向左第 i i i位: v = c w c w − 1 . . . c i + 1 1 c i − 1 . . . 1. v=c_wc_{w-1}...c_{i+1}1c_{i-1}...1. v=cwcw1...ci+11ci1...1.设左边的 w − i w-i wi ( c w . . . c i + 1 ) (c_w...c_{i+1}) (cw...ci+1) x x x,右边的 i − 1 i-1 i1 ( c i − 1 . . . c 1 ) (c_{i-1}...c_1) (ci1...c1) y . y. y.

( 1 ) (1) (1) x < d w . . . d i + 1 x<d_w...d_{i+1} x<dw...di+1时,这时无论 y y y是什么值 v v v都不会大于 n n n.因此此时 x x x [ 0 , d w − 1 ] [0,d_w-1] [0,dw1] d w d_w dw种选法, y y y [ 00...0 , 99...9 ] [00...0,99...9] [00...0,99...9] 1 0 l e n ( y ) − 1 10^{len(y)-1} 10len(y)1种选法。举个例子:当 n = 2022 n=2022 n=2022时,要统计 i = 2 i=2 i=2时(即十位为1的贡献), c 3 c 2 1 c 1 c_3c_21c_1 c3c21c1中的 x = c 3 c 2 < 20 x=c_3c_2<20 x=c3c2<20时(共 20 20 20种选法), c 1 c_1 c1都有 [ 0 , 9 ] [0,9] [0,9] 10 10 10种选法。因此 i = 2 i=2 i=2的贡献为 20 ∗ 10 = 200 20*10=200 2010=200
( 2 ) (2) (2) x = d w . . . d i + 1 x=d_w...d_{i+1} x=dw...di+1时,这时 y y y不能超过 d i − 1 . . . d 1 . d_{i-1}...d_1. di1...d1.因此此时贡献为 x x x固定, y y y [ 00...0 , d i − 1 . . . d 1 ] [00...0,d_{i-1}...d_1] [00...0,di1...d1]的方案数,即 ( d i − 1 . . . d 1 ) + 1. (d_{i-1}...d_1)+1. (di1...d1)+1.上面的例子中此情况的贡献为 9 + 1 = 10 9+1=10 9+1=10,因此 i = 2 i=2 i=2时的总贡献为 ( 1 ) + ( 2 ) = 200 + 10 = 210. (1)+(2)=200+10=210. (1)+(2)=200+10=210.

上面考虑的情况为 1 < i < w 1<i<w 1<i<w时的情况,当 i = 1 i=1 i=1 i = w i=w i=w时,情况类似:
( a ) (a) (a) i = 1 i=1 i=1时,仍有 ( 1 ) (1) (1)情况中的贡献;仍需要判断当 d 1 ≥ 1 d_1\ge1 d11时, c w c w − 1 . . . c 2 1 c_wc_{w-1}...c_21 cwcw1...c21也在 [ 1 , n ] [1,n] [1,n]中,答案再加 1. 1. 1.
( b ) (b) (b) i = w i=w i=w时,当 d w = 1 d_w=1 dw=1时(可以保证 d w ≠ 0 d_w\ne0 dw=0), y y y可以取到 [ 00...0 , d i − 1 . . . d 1 ] [00...0,d_{i-1}...d_1] [00...0,di1...d1]中的任何值,贡献为 ( d i − 1 . . . d 1 ) + 1. (d_{i-1}...d_1)+1. (di1...d1)+1.否则 d w ≠ 1 , d_w\neq1, dw=1,此时没有限制,贡献为 1 0 w − 1 10^{w-1} 10w1.

代码

class Solution {
public:
    int countDigitOne(int n) {
        int sum = n;
        int len = ceil(log10(n + 1));//即上面提到的w
		
		//left=d[w]...d[i+1],right=d[i-1]...d[1]
        int left = n / 10;
        int right = 0;
        int ten[] = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};//存10的幂

        char buf[11];
        int ptr = 0;

		//先用sum把n存到数组中
        while(sum) {
            buf[++ptr] = (sum%10) + '0';
            sum /= 10;
        }
        for(int i = 1; i <= len; i++) {
            if(i == 1) {
                sum += left * ten[i - 1];
                if(buf[i] >= '1') sum++; 
            }
            else if(i == len) {
                if(buf[i] == '1') sum += right + 1;
                else sum += ten[i - 1];
            }
            else {
                sum += left * ten[i - 1];
                if('1' < buf[i]) sum += ten[i - 1];
                else if('1' == buf[i]) sum += right + 1;
            }
            //更新left和right
            left /= 10;
            right += (buf[i] - '0') * ten[i - 1];
        }
        return sum;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值