zoukankan      html  css  js  c++  java
  • 计算1至n中数字X出现的次数

    计算1至n中数字X出现的次数

    描述

    计算 1 至 n 中数字 X 出现的次数,其中 n1,X[0,9]

    解题思路

    这是一道比较简单的题目,举个例子先:假设 n=11,X=1,那么就是求 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 这 11 个数字中 1 出现的次数,很容易能看出来结果为 4,在 1 和 10 中各出现了一次,在 11 中出现了两次。

    最简单的办法就是依次遍历 1 至 n,再分别求每个数字中 X 出现的次数,代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #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 ", count(237, 1));
    }

    这个方法的缺点是时间复杂度太高,countOne 方法的时间复杂度是 O(log10n),count 方法的时间复杂度是 O(nlog10n)

    一个更好的办法是利用数学公式直接计算出最终的结果,该方法是依次求出数字 X 在个位、十位、百位等等出现的次数,再相加得到最终结果。这里的 X[1,9],因为 X=0 不符合下列规律,需要单独计算。

    首先要知道以下的规律:

    • 从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
    • 从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
    • 从 1 至 1000,在它们的千位数中,任意的 X 都出现了 100 次。

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

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

    接下来以 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 的出现次数。

    总结一下以上的算法,可以看到,当计算右数第 i 位包含的 X 的个数时:

    1. 取第 i 位左边(高位)的数字,乘以 10i1,得到基础值 a
    2. 取第 i 位数字,计算修正值
      1. 如果大于 X,则结果为 a+10i1
      2. 如果小于 X,则结果为 a
      3. 如果等 X,则取第 i 位右边(低位)数字,设为 b,最后结果为 a+b+1

    相应的代码非常简单,效率也非常高,时间复杂度只有 O(log10n)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 计算数字 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;
    }

    当 X = 0 时,规律与上面给出的规律不同,需要另行考虑。

    最主要的区别是,最高位中永远是不会包含 0 的,因此,从个位累加到左起第二位就要结束,需要将上面代码中 for 循环的判断条件改为 k / 10 != 0。

    其次是,第 i 位的基础值不是高位数字乘以 10i1,而是乘以 10i11。以 1 至 102 为例,千位中实际包含 3 个 0,但这三个 0 是来自于个位 2 计算得到的修正值,而非来自于基础值。千位的基础值是 0,因为不存在数字 01, 02, 03, ..., 09,即数字前是没有前导 0 的。解决办法就是将上面代码中第 6 行改为 cnt += (k / 10 - 1) * i。

    经过综合与化简,得到了以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 计算数字 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;
    }

    主要是将一些步骤进行了合并,令代码比较简练。

  • 相关阅读:
    中国历史朝代公元对照简表
    [Solved] DashBoard – Excel Service: The data sources may be unreachable, may not be responding, or may have denied you access.
    Delete/Remove Project from TFS 2010
    Sharepoint site showing system account instead of my username on the top right corner.
    你的成功在于你每天养成的习惯
    Internet Information Services is running in 32bit emulation mode. Correct the issue listed above and rerun setup.
    Prepare to back up and restore a farm (Office SharePoint Server 2007)
    Word中字号与磅值的对应关系
    How to: Change the Frequency for Refreshing the Data Warehouse for Team System
    UI Automation in WPF/Silverlight
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3309444.html
Copyright © 2011-2022 走看看