• 剑指Offer_#43_1~n整数中1出现的次数


    剑指Offer_#43_1~n整数中1出现的次数

    Contents

    题目

    输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
    例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

    示例 1:

    输入:n = 12
    输出:5

    示例 2:

    输入:n = 13
    输出:6

    限制:

    1 <= n < 2^31

    思路分析

    这题参考了评论区大佬的题解,以下的图解也是搬运过来的。

    整体的思路是,把在每一位上出现1的次数计算出来,然后相加。
    那么问题关键就在于如何计算每一位出现1的次数

    一个结论:如何数出每一位出现1的次数?

    如何计算0010~2219范围内,1在十位出现的次数?
    首先可以把0010~2219范围内,十位是1的所有数字枚举出来。

    0010,0011,...0019(1在十位出现10次)
    0110,0111,...0119(1在十位出现10次)
    ...
    0910,0911,...0919(1在十位出现10次)
    1010,1011,...1019(1在十位出现10次)
    ...
    1910,1911,...1919(1在十位出现10次)
    2010,2011,...2019(1在十位出现10次)
    2110,2111,...2119(1在十位出现10次)
    2210,2211,...2219(1在十位出现10次)

    可以看到高位(前两位)的变化范围是00~22,有23种选择。
    固定高位是某一种情况时,低位(个位)变化范围是0~9,有10种选择。
    高位和低位组合,就有23*10=230种组合。
    总结规律,你会发现,其实可以把0010~2219里的十位数字直接“抠”掉,剩下的就是000~229,一共就是230个数字。
    因为十位上面的1已经是固定的,所以其实000~229里的每个数,和上面所枚举的230个数字是一一对应的。比如000对应着0010,001对应着0011...所以000~229范围内数字的个数,就是0010~2219范围内十位出现1的数字个数。

    寻找规律,总结为数学表达式

    理解了上面的结论,可以观察到以下的规律(图解来自评论区题解)

    cur == 0


    cur == 1


    cur > 1


    循环变量的初始化和更新

    初始化

    high = n // 10
    cur = n % 10
    low = 0
    digit = 1 # 个位

    更新

    while high != 0 or cur != 0: # 当 high 和 cur 同时为 0 时,说明已经越过最高位,因此跳出
       low += cur * digit # 将 cur 加入 low ,组成下轮 low
       cur = high % 10 # 下轮 cur 是本轮 high 的最低位
       high //= 10 # 将本轮 high 最低位删除,得到下轮 high
       digit *= 10 # 位因子每轮 × 10

    解答

    class Solution {
        public int countDigitOne(int n) {
            int res = 0;
            //初始化高位high,低位low,当前位cur
            int high = n / 10;//除去个位
            int cur = n % 10;//个位
            int low = 0;//个位没有低位
            //digit表示当前位的数量级,比如个位的digit是1,十位的digit是10
            int digit = 1;
            //high和cur同时为0,表明已经越过了最高位,停止循环
            while(high != 0 || cur != 0){
                //计算cur位上出现1的次数
                if(cur == 0) res += high * digit;
                else if(cur == 1) res += high * digit + low + 1;
                else res += (high + 1) * digit;
                //更新变量low,high,cur,digit
                low += cur * digit;//cur变成low的一部分
                cur = high % 10;//高位的最后一位变成cur
                high /= 10;//high去除最后一位
                digit *= 10;//数量级乘10
            }
            return res;
        }
    }

    时间复杂度是

  • 相关阅读:
    传递闭包+求概率——列项相消法lightoj1321好题
    TSP+期望——lightoj1287记忆化搜索,好题!
    高斯消元+期望dp——light1151
    异或前缀和,组合数学——cf1054D
    数论GCD——cf1055C
    字符串哈希——1056E
    区间dp——cf1025D二叉搜索树的中序遍历好题!
    集合划分——cf1028D思维题
    线性dp——求01串最大连续个数不超过k的方案数,cf1027E 好题!
    java_and_tomcat_set_environment
  • 原文地址:https://www.cnblogs.com/Howfars/p/13305655.html
Copyright © 2020-2023  润新知