#计算1~N中数字X的个数,X=0~9.

计算1~N中数字X的个数,X=0~9.

转载

解题思路一:
遍历所有数字


#include <stdio.h>
// 计算数字 X 在 n 中出现的次数。

int countOne(int n, int x) {

    int cnt = 0;

    for (;n > 0;n /= 10) {

        if (n % 10 == x) {

            cnt++;

        }

    }

    return cnt;

}

// 计算数字 X 在 1-n 中出现的次数。

int count(int n, int x) {

    int cnt = 0;

    for (int i = 1;i <= n;i++) {

        cnt += countOne(i, x);

    }

    return cnt;

}

int main() {

    printf("%d\n", count(237, 1));

}

解题思路二:

分别求出个位、十位、百位……上X出现的次数

举个例子
首先要知道以下的规律:
•从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
•从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
•从 1 至 1000,在它们的千位数中,任意的 X 都出现了 100 次。

依此类推,从 1 至 10 i ,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10 i−1 次。

这个规律很容易验证,这里不再多做说明。

接下来以 n=2593,X=5 为例来解释如何得到数学公式。从 1 至 2593 中,数字 5 总计出现了 813 次,其中有 259 次出现在个位,260 次出现在十位,294 次出现在百位,0 次出现在千位。

现在依次分析这些数据,首先是个位。从 1 至 2590 中,包含了 259 个 10,因此任意的 X 都出现了 259 次。最后剩余的三个数 2591, 2592 和 2593,因为它们最大的个位数字 3 < X,因此不会包含任何 5。

然后是十位。从 1 至 2500 中,包含了 25 个 100,因此任意的 X 都出现了 25×10=250 次。剩下的数字是从 2501 至 2593,它们最大的十位数字 9 > X,因此会包含全部 10 个 5。最后总计 250 + 10 = 260。

接下来是百位。从 1 至 2000 中,包含了 2 个 1000,因此任意的 X 都出现了 2×100=200 次。剩下的数字是从 2001 至 2593,它们最大的百位数字 5 == X,这时情况就略微复杂,它们的百位肯定是包含 5 的,但不会包含全部 100 个。如果把百位是 5 的数字列出来,是从 2500 至 2593,数字的个数与百位和十位数字相关,是 93+1 = 94。最后总计 200 + 94 = 294。

最后是千位。现在已经没有更高位,因此直接看最大的千位数字 2 < X,所以不会包含任何 5。到此为止,已经计算出全部数字 5 的出现次数。

计算1-9出现的次数


// 计算数字 X 在 1-n 中出现的次数。

int count(int n, int x) {

    int cnt = 0, k;

    for (int i = 1;k = n / i;i *= 10) {

        // k / 10 为高位的数字。

        cnt += (k / 10) * i;

        // 当前位的数字。

        int cur = k % 10;

        if (cur > x) {

            cnt += i;

        } else if (cur == x) {

            // n - k * i 为低位的数字。

            cnt += n - k * i + 1;

        }

    }

    return cnt;

}

计算0出现的次数

// 计算数字 0 在 1-n 中出现的次数。

int countZero(int n) {

int cnt = 0, k;

// k / 10 为高位的数字。

for (int i = 1;(k = n / i) / 10;i *= 10) {

    cnt += (k / 10) * i;

    // k % 10 为当前位的数字。

    if (k % 10 == 0) {

        // n - k * i 为低位的数字。

        cnt += n - k * i + 1 - i;

    }

}

return cnt;

}

将以上整合一下

// 计算数字 X 在 1-n 中出现的次数。

int count(int n, int x) {

int cnt = 0, k;

for (int i = 1;k = n / i;i *= 10) {

    // 高位的数字。

    int high = k / 10;

    if (x == 0) {

        if (high) {

            high--;

        } else {

            break;

        }

    }

    cnt += high * i;

    // 当前位的数字。

    int cur = k % 10;

    if (cur > x) {

        cnt += i;

    } else if (cur == x) {

        // n - k * i 为低位的数字。

        cnt += n - k * i + 1;

    }

}

return cnt;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值