文章标题

这道题目与leetcode 600 有一些相似, 所以博主一开始用的是相类似的解法. dp[n][i]记录的是位数<=n的数中, 包含i个1的数的个数.
1. dp[n][0] = dp[n-1][0] + 8*dp[n-1][0] = dp[n-1][0] * 9.
其中, 第一部分表示位数<=n-1的数中, 包含0个1的个数. 第二部分表示位数等于n的数, 包含0个1的个数. 第n位可以选择2, 3, …9这八个数.
2. dp[n][i] = dp[n-1][i] + dp[n-1][i-1] + 8*dp[n-1][i]
其中第一部分表示位数小于等于n-1的包含i个1的数的数量. 第二部分表示位数为n, 且第n位为1. 则剩下的部分只要满足有[i-1]个1就可以了. 第三部分表示位数也是n位. 但是第n位选择2, 3, … 9


假设n的位数为N,在得到这个dp之后我们可以把问题拆解为两部分: 位数为N的数中包含的1数量. 位数小于N的数中包含的1的数量. 第二部分很好求:

for(int c = len-1; c>=1; --c) {
    cnt += dp[len-1][c]*c;
}

zhzz至于第一部分, 因为必须<=n, 所以我们需要逐一位处理:
先处理小于n的数,
考虑第N位:

5314
4xxx
3xxx
2xxx
1xxx
第N位可以取4, 3, 2, 1

剩下的位:

5314
52xx
51xx
50xx
第N-1可以取2, 1, 0

int countDigitOne(int n) {
    if(0 == n) {
        return 0;
    }
    int len = 1; 
    long mask = 10;
    while(n >= mask) {
        ++len;
        mask *= 10;
    }


    vector<vector<int>> dp(len+1, vector<int>(len+1, 0));
    dp[0][0] = 1;
    dp[1][0] = 9;
    dp[1][1] = 1;
    for(int l = 2; l <= len; ++l) {
        dp[l][0] = dp[l-1][0] * 9;
        for(int c = 1; c <= l; ++c) {
            dp[l][c] = dp[l-1][c] * 9 + dp[l-1][c-1];
        }
    }

    mask /= 10;
    int cnt = 0;
    /*size = len*/
    int prev_1_cnt = 0;
    for(int i = len; i >= 1; --i) {
        const int digit = n/mask;
        int not1_head = 0;
        if(i == len) {
            not1_head = digit - 2;
        }else if(digit == 1){
            not1_head = 1;
        }else {
            not1_head = digit - 1;
        }
        if(not1_head >= 1) {
            for(int l = 0; l <= i-1; ++l) {
                cnt += not1_head * dp[i-1][l] * (prev_1_cnt+l);
            }
        }
        if(digit > 1) {
            for(int c = 0; c <= i-1; ++c) {
                cnt += dp[i-1][c] * (prev_1_cnt + c +1);
            }
        }

        n = n % mask;
        mask /= 10;
        if(digit == 1) {
            ++prev_1_cnt;
        }
    }
    /*size < len*/
    for(int c = len-1; c>=1; --c) {
        cnt += dp[len-1][c]*c;
    }

    cnt += prev_1_cnt;
    return cnt;
}

a前面的方法还是非常繁琐的. 这里再引入StefanPochmann 大神的解法

对于一个数. 我们计算能有多少个数个位上有1, 十位上有1, 百位上有1…把这些数都加起来就是我们要的结果:
a我们只分析百位上的情况:
n=3141592

3141592
0-3141|1|0-99
总共有(3141+1)*100个数

n=3141192

3141192
0-3140|1|0-99
3141|1|0-91
总共有3141*100 + 92

能n=3141092

3141092
0-3140|1|0-99
共有3141*100

int countDigitOne(int n) {
    if(0 == n) {
        return 0;
    }
    long mask = 1;
    int cnt = 0;
    while(n >= mask) {
        const int a = n / mask;
        const int b = n % mask;
        const int v = a%10;
        if(v > 1) {
            cnt += (a/10 + 1) * mask;
        }else if(v == 1) {
            cnt += (a/10) * mask + b+1;
        }else {
            cnt += (a/10) * mask;
        }
        mask *= 10;
    }
    return cnt;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值