求从0到n这n+1个整数的十进制表示中出现m的次数, 其中(9>=m>=0)

本文介绍了一种算法用于计算从0到n整数范围内的特定数字出现的次数,包括从最高位到最低位的所有位置。通过分析不同位数的情况,实现高效计算。

网上看到一道题:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数‘

 

这里修改下题目:求从0到n这n+1个整数的十进制表示中出现m的次数, 其中(9>=m>=0)


例如n为12、m为1:从0到12这些整数中包含1的数字有1、10、11和12,一共有5个1

 

代码如下:

#include <cstdio>

/******************************************************************
 * 获知[0, iBound]中所有数字的十进制表达式中包含的0的总数
 ******************************************************************/
int GetZeroCount(int iBound)
{
    if (0 > iBound)
    {
        return(0);
    }

    if (0 == iBound)
    {
        return(1);
    }

    /*
     * 下面以GetZeroCount(300456)为例分析问题
     */

    /*
     * [0, 300456]可拆分为:
     * [0, 99999], [100000, 199999], [200000, 299999], [300000, 300456]
     */

    /*
     * iBase记录与iBound的位数相同的最小的正整数
     * iBits记录iBase中0的个数
     * 这里iBound = 300456, 则有: iBase = 100000, iBits = 5
     */
    int iBase = 1;
    int iBits = 0;
    int iTemp = iBound / 10;
    while (iTemp > 0)
    {
        iBase *= 10;
        iBits += 1;
        iTemp /= 10;
    }

    int iHighest = iBound / iBase;
    int iLeft = iBound % iBase;

    /*
     * 计算[0, 99999]中0的总数
     */
    int iStartCount = GetZeroCount(iBase - 1);

    /*
     * 计算[100000, 199999]中0的总数
     * 其中: 0在第2位的情形为:
     * 第1位始终为1不变, 第2位始终为0不变, 其他4位都可取[0, 9]这10种值,
     * 故0在第2位的情形有: 1 * 1 * 10 * 10 * 10 * 10 = 10000种
     * 具体为: [100000, 109999], 这10000种
     * 同理可知: 0在第3, 4, 5, 6位也都有10000种情形
     * 故而, [100000, 199999]中0的总数为50000个
     */

    /*
     * [200000, 299999]中0的总数与[100000, 199999]中的相同
     */

    /*
     * 计算[100000, 199999], [200000, 299999]中0的总数
     */
    int iMiddleCount =  (iBase / 10) * iBits * (iHighest - 1);

    /*
     * iHighZeroCount记录与最高位相邻的0的个数
     */
    int iHighZeroCount = 0;
    if (0 == iLeft)
    {
        iHighZeroCount = iBits;
    }
    else
    {
        int iCheck = iLeft * 10;
        while (iCheck < iBase)
        {
            iHighZeroCount += 1;
            iCheck *= 10;
        }
    }

    /*
     * iLeftBits记录iLeft的位数
     */
    int iLeftBits = iBits - iHighZeroCount;

    /*
     * 计算[300000, 300456]中0的总数
     * 最高位后面两个0共计出现(456 + 1)次
     * 后面三位出现0的情形等价于[000, 456]中0的总数
     * [000, 456]比[0, 456]多的0在数字开头
     * 其中:
     * 两个0开头的情形有10个: [000, 009]
     * 一个0开头的情形有90个: [010, 099]
     * 继而可知[300000, 300456]中0的总数为:
     * (456 + 1) * 2 + GetZeroCount(456) + 10 * 2 + 90 * 1
     * 10拆为9 + 1, 可得:
     * (456 + 1) * 2 + GetZeroCount(456) + 2 + 9 * 2 + 90 * 1
     */
    int iLastCount = (iLeft + 1) * iHighZeroCount + GetZeroCount(iLeft);

    iLastCount += (iLeftBits - 1);
    int iRedundantZeroCount = 9;
    while (iLeftBits > 1)
    {
        iLeftBits -= 1;
        iLastCount += iLeftBits * iRedundantZeroCount;
        iRedundantZeroCount *= 10;
    }

    return(iStartCount + iMiddleCount + iLastCount);
}

/******************************************************************
 * 获知[0, iBound]中所有数字的十进制表达式中包含的iNumber的总数
 ******************************************************************/
int GetNumberCount(int iNumber, int iBound)
{
    if (0 > iNumber || 9 < iNumber)
    {
        return(-1);
    }

    /*
     * 因为0不可能排在最高位
     * 所以, [100, 199]在不计<最高位>的iNumber时不等于[0, 99]的iNumber数
     * 继而, 0的总数计算方法需要特殊处理
     */
    if (0 == iNumber)
    {
        return(GetZeroCount(iBound));
    }

    if (0 >= iBound)
    {
        return(0);
    }

    /*
     * iBase记录与iBound的位数相同的最小的正整数
     * 如: iBound = 345时, iBase = 100
     */
    int iBase = 1;
    int iTemp = iBound / 10;
    while (iTemp > 0)
    {
        iBase *= 10;
        iTemp /= 10;
    }

    int iHighest = iBound / iBase;
    int iLeft = iBound % iBase;

    /*
     * 计算最高位上包含的iNumber的个数
     */
    int iHighCount = 0;
    if (iHighest > iNumber)
    {
        /*
         * 如iHighest = 3, iNumber = 1
         * 此时[0, 345]中, iNumber在最高位的数字包括[100, 199]
         * 共计100个, 即iBase
         */
        iHighCount = iBase;
    }
    else if (iHighest < iNumber)
    {
        /*
         * 如iHighest = 3, iNumber = 4
         * 此时[0, 345]中, iNumber在最高位的数字个数为0
         */
        iHighCount = 0;
    }
    else
    {
        /*
         * 如iHighest = 3, iNumber = 3
         * 此时[0, 345]中, iNumber在最高位的数字包括[300, 345]
         * 共计46个, 即iLeft + 1
         */
        iHighCount = iLeft + 1;
    }

    /*
     * 下面都<不能再计算最高位>的iNumber的个数
     */

    /*
     * 计算[0, iHighest * iBase - 1]中所有<不计最高位的>iNumber的个数
     * 如上即为: [0, 299]中不计最高位的iNumber的个数
     * [0, 299] 可拆分为: [0, 99], [100, 199], [200, 299]
     * [100, 199]与[200, 299]在<不计最高位>的iNumber(1~9)的个数时与[0, 99]是等价的
     * 即有: 3 * GetNumeralCount(iNumber, 99)
     */
    int iFullCount = iHighest * GetNumberCount(iNumber, iBase - 1);

    /*
     * 计算[iHighest * iBase, iBound]中所有<不计最高位>的iNumber的个数
     * 如上即为: [300, 345]中<不计最高位>的iNumber的个数
     * [300, 345]在<不计最高位>的iNumber的个数与[0, 45]是等价的
     * 即有: GetNumberCount(iNumber, 45)
     */
    int iLeftCount = GetNumberCount(iNumber, iLeft);

    return(iHighCount + iFullCount + iLeftCount);
}

/******************************************************************
 * 获知iCheck的十进制表达式中包含的iNumber的总数
 ******************************************************************/
int GetNumberCountOf(int iNumber, int iCheck)
{
    if (0 > iNumber || 9 < iNumber)
    {
        return(-1);
    }

    if (0 > iCheck)
    {
        return(-1);
    }

    int iCount = 0;

    do
    {
        if (iCheck % 10 == iNumber)
        {
            iCount += 1;
        }
        iCheck /= 10;
    } while (iCheck > 0);

    return(iCount);
}

/******************************************************************
 * 利用枚举法, 获知[0, iBound]中所有数字的十进制表达式中包含的iNumber的总数
 ******************************************************************/
int EnumerateNumberCount(int iNumber, int iBound)
{
    if (0 > iNumber || 9 < iNumber)
    {
        return(-1);
    }

    if (0 > iBound)
    {
        return(-1);
    }

    int iCount = 0;

    for (int iIndex = 0; iIndex <= iBound; ++iIndex)
    {
        iCount += GetNumberCountOf(iNumber, iIndex);
    }

    return(iCount);
}

void test_way_1(int iBound)
{
    for (int iNumber = 0; iNumber < 10; ++iNumber)
    {
        int iCount = GetNumberCount(iNumber, iBound);
        printf("%5d ----- %5d\n", iNumber, iCount);
    }
}

void test_way_2(int iBound)
{
    for (int iNumber = 0; iNumber < 10; ++iNumber)
    {
        int iCount = EnumerateNumberCount(iNumber, iBound);
        printf("%5d ----- %5d\n", iNumber, iCount);
    }
}

int main(int argc, char * argv[])
{
    int iBound = 300456;
    printf(" Get Number Count Of [0, %d]\n", iBound);

    printf("---------- test_way_1 ----------\n");
    test_way_1(iBound);

    printf("---------- test_way_2 ----------\n");
    test_way_2(iBound);

    printf("------------ finish ------------\n");
    return(0);
}


 

<think>根据引用[1]和[2]的描述,用户最初的需是:输入正整数n和09范围内的一个数m,判断m是否存在于n中,若不存在则输出提示;若存在则删除n中的m构成新数k,然后计算n除以k的倍数(保留两位小数),并输出k和倍数。 但用户当前的问题已经改变:用户现在想要获取一个能计算给定正整数n中数字m(m取值范围为[0,9])出现次数的解决方案。 因此,我们需要调整方向,不再做删除数字构成新数并计算倍数的操作,而是计算数字m在正整数n中出现的次数。 思路: 1. 将正整数n当作一个字符串来处理,遍历字符串的每一位,判断每一位是否等于字符m(注意:m是09的数字,需要将整数m转换为字符形式)。 2. 或者,通过数学方法,不断取n的每一位数字(通过除以10取余),然后与m比较,统计次数。 这里我采用第二种方法(数学方法): 步骤: a. 初始化计数器count=0 b. 循环条件:当n>0 c. 取当前最低位:digit = n % 10 d. 判断digit是否等于m,如果相等则count++ e. 然后将n除以10整数除法)以去掉最低位:n = n / 10 f. 循环直到n为0 注意:当m为0时,需要特殊考虑吗? 实际上,上述方法对于0也是有效的,因为当n的某一位为0时,digit为0,与m(0)相等,计数器会增加。但是,如果n本身就是0,那么我们需要单独处理,因为0%10=0,然后0/10还是0,会陷入死循环?或者一次也不执行循环?因此,如果n为0,那么我们需要判断m是否为0,如果是,则出现1次,否则0次。 但是,题目要n是正整数,所以n至少为1,因此不用考虑0的情况。 然而,题目中m的取值范围是09,所以m可以是0,而n是正整数(不可能有前导0),所以我们在循环中处理0是没问题的。例如:n=102,m=0,则取最低位:2(不等于0),然后除以1010;再取0,等于0,计数加1;再除以101;再取1不等于0,结束。所以计数为1。 但是,还有一种特殊情况:当n=0时?题目要整数n,所以我们假设n>=1。但为了程序健壮性,如果用户输入0,我们可以按0处理。不过题目要是正整数,所以我们可以不考虑0。 因此,我们按照步骤写一个函数,函数名为count_digit,参数为整数n和整数m(0<=m<=9),返回值为m在n中出现的次数。 示例: 输入:n=112233, m=1 -> 返回2 输入:n=100, m=0 -> 返回2 代码实现(C语言): 注意:由于题目要是正整数,所以n>0,因此我们可以直接使用循环。 函数定义: int count_digit(int n, int m) { int count = 0; if (n == 0) { // 虽然题目要整数,但为了健壮性,考虑n=0的情况 return m == 0 ? 1 : 0; } while (n > 0) { int digit = n % 10; if (digit == m) { count++; } n /= 10; } return count; } 但是,题目要是正整数,所以n>0,那么我们可以省略n=0的判断。 然而,如果n是0,而题目要整数,所以我们可以不考虑。但如果用户输入0,那么我们可以按照要(正整数)拒绝,但题目没有要处理输入错误,所以我们假设输入是正整数。 因此,我们可以简化为: int count_digit(int n, int m) { int count = 0; while (n != 0) { int digit = n % 10; if (digit == m) { count++; } n = n / 10; } return count; } 但是注意:这样写的话,如果n是0,循环不会执行,返回0。而实际上,如果n=0,m=0,应该返回1,但题目要整数,所以n不会为0,所以这样写没问题。 但是,如果用户输入的n就是0(虽然题目要整数,但用户可能输入0),我们可以按照引用[1][2]中的描述,要输入正整数。不过现在用户只是要计算出现次数,我们可以先按上述函数实现。 然而,用户并没有要写完整程序,而是要一个解决方案。我们可以描述这个算法,并给出代码示例。 另外,引用[3]中给出了一个计算一个数能被另一个数整除的个数的方法,但这个问题和我们现在的问题不同,所以仅作参考。 引用[4]中的代码是关于猜数字和乘方计算的,与当前问题无关。 综上所述,我们按照第二种方法(数学方法)来给出解决方案。 但是,用户可能要多种语言的实现?但问题中没有明确,这里按照最常见的C语言实现。 如果用户需要其他语言,可以后续补充。 因此,我们可以这样回答: 要计算正整数n中数字m(0~9)出现的次数,可以使用循环取出n的每一位数字,并与m进行比较,然后计数。具体步骤如下: 1. 初始化计数器count为0。 2. 当n不为0时循环: a. 取n的个位数字(用n % 10得到)。 b. 如果该数字等于m,则计数器count加1。 c. 将n整除10(即n = n / 10),以去掉个位数字。 3. 循环结束后,count即为m在n中出现的次数。 注意:由于n是正整数,循环会在n变为0时终止。 以下是一个C语言函数实现: ```c #include <stdio.h> int count_digit(int n, int m) { int count = 0; while (n != 0) { int digit = n % 10; if (digit == m) { count++; } n = n / 10; } return count; } int main() { int n, m; printf("请输入正整数n: "); scanf("%d", &n); printf("请输入0~9的数字m: "); scanf("%d", &m); int count = count_digit(n, m); printf("数字%d在正整数%d中出现了%d次。\n", m, n, count); return 0; } ``` 但是,用户可能要不能使用循环(比如递归),或者要处理大整数(超出int范围)?但根据引用中的描述,输入是正整数,且没有特别说明范围,我们假设在int范围内。 如果n非常大(比如超出long long范围),那么我们可以用字符串处理(如思路1),但是题目没有要,所以我们给出常规方法。 另外,如果用户要使用递归,我们可以提供一个递归版本: 递归函数: int count_digit_recursive(int n, int m) { if (n == 0) { return 0; } int digit = n % 10; int count = (digit == m) ? 1 : 0; return count + count_digit_recursive(n / 10, m); } 但是,递归深度与n的位数有关,对于较大的n可能会导致栈溢出,所以循环更安全。 根据用户需,我们提供循环版本。 最后,生成相关问题: 1. 如何计算一个正整数中所有数字(0~9)各自出现的次数? 2. 如何计算一个正整数中某个数字出现的次数(不使用循环)? 3. 如何统计一个字符串中某个字符出现的次数? 4. 如何处理大整数(超过long long范围)中数字出现的次数? 注意:引用中的原始问题(删除数字构成新数并计算倍数)和当前问题不同,但用户已经转向了新的问题。 因此,回答完毕。 </think>### 计算数字出现次数的方法 要计算正整数 \(n\) 中数字 \(m\)(\(m \in [0,9]\))的出现次数,可采用以下两种常用方法: #### 方法一:数学取模法(推荐) 1. **初始化计数器**:`count = 0` 2. **循环处理**: - 取 \(n\) 的最低位:\(digit = n \% 10\) - 若 \(digit == m\),则计数器加 1 - 去掉最低位:\(n = n / 10\)整数除法) 3. **终止条件**:当 \(n = 0\) 时结束 4. **时间复杂度**:\(O(\log_{10} n)\) ```c #include <stdio.h> int count_digit(int n, int m) { int count = 0; while (n > 0) { if (n % 10 == m) count++; // 比较最低位 n /= 10; // 移除最低位 } return count; } int main() { int n = 202405; int m = 0; printf("出现次数:%d\n", count_digit(n, m)); // 输出:2 return 0; } ``` #### 方法二:字符串转换法 1. 将整数 \(n\) 转换为字符串 2. 遍历字符串的每个字符 3. 若字符等于 \(m + &#39;0&#39;\)(ASCII 转换),则计数器加 1 ```c #include <stdio.h> #include <string.h> int count_digit_str(int n, int m) { char str[20]; sprintf(str, "%d", n); // 数字转字符串 int count = 0; for (int i = 0; i < strlen(str); i++) { if (str[i] == m + &#39;0&#39;) count++; // 比较字符 } return count; } ``` ### 关键说明 1. **特殊情况处理**: - 若 \(n = 0\) 且 \(m = 0\),直接返回 1 - \(m\) 必须在 [0,9] 范围内 2. **示例验证**: - \(n = 1050, m = 0\) → 出现 2 次(方法一输出) - \(n = 999, m = 9\) → 出现 3 次(方法二输出) ### 相关问题 1. 如何优化大整数(超过 `long long` 范围)的数字统计? 2. 递归实现数字统计的方法是什么? 3. 如何统计十进制数中所有数字(0-9)的出现频率? 4. 若需排除前导零(如 00123),应如何修改算法? [^1]: 输入正整数和数字进行判断和操作的规范说明 [^2]: 数字存在性检查及后续处理流程 [^3]: 整数除法与计数机制的数学原理 [^4]: C语言基础操作参考
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值