题解 | 整数中1出现的次数

整数中1出现的次数(从1到n整数中1出现的次数)

https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6

找规律的题,官方的题解看得云里雾里,自己找了一下规律,这题有点动态规划的意思。

找规律

首先就是将0 ~ n分段,分段的原则按位数,如

  • 1位数:0 ~ 9
  • 2位数:10 ~ 99
  • 3位数:100 ~ 999
  • ...

分段之后,使用表格查看1出现的规律:

以n = 50345为例,分段如下:

从表格可以看到,当前位数由两部分构成,以100 ~ 999为例:

  • 最高位固定1,计算1出现在最高位的次数
  • 1出现在100 ~ 199中100次,正好时当前的base,即100
  • 计算1出现在后续位的个数
  • 后续位,包含100 ~ 199,200 ~ 299,... , 900 ~ 999共9个0 ~ 99
  • 0 ~ 99中,1出现的个数是0 ~ 9,10 ~ 99的累加即20

可知100 ~ 999中,1出现的次数是:当前base 100 + 9个0 ~ 99。

所以0 ~ 999中,再加上一个0 ~ 99,即base 100 + 10个0 ~ 99。

可以总结出规律:

  • 0 ~ 9:1的个数是1
  • 0 ~ 99:1的个数是10 + 10个 0 ~ 9
  • 0 ~ 999:1的个数为:100 + 10个 0 ~ 99

以此类推,0 ~ 99999的个数是10000 + 10个0 ~ 9999。

10000 ~ 50345不满足完整的规律,需要再细分:

  • 10000 ~ 19999有10000次
  • 不够9个0 ~ 9999,只有四个
  • 50000 ~ 50345有345个数,所以递归求345有多少个1
  • 中间的0都需要跳过

代码实现

step1:先计算每个完整base下的1出现的次数,并存储到HashMap备用

base定义:

  • 1:0 ~ 9
  • 10: 0 ~ 99
  • 100:0 ~ 999
  • ...

把每个可以到999...的称为完整base。

step2:递归计算最高base中1出现的次数,即上一个base是完整的,当前最高base并不完整

根据最高位是否大于1有不同的路径:

  • 大于1:如最高位是5,则个数是5 * 上一个完整base + 当前base(10/100/1000...) + 去掉最高位后递归
  • 等于1:上一个完整base + 去掉最高位后的数字 + 1
  • 小于1:递归中不允许出现的情况

递归计算下一个base,如果下一位是0,则跳过,直到出现不为0位,再进行递归。

java代码:

import java.util.*;
public class Solution {
    HashMap<Integer, Integer> dp = new HashMap<>();
    public int NumberOf1Between1AndN_Solution(int n) {
        int maxBase = 1;
        int count = 1;
        dp.put(maxBase, count);
        while (n / maxBase >= 10) {
            maxBase *= 10;
            count = maxBase + 10 * count; // 当前base中1的个数为 base + 10 * 上一个base个数
            dp.put(maxBase, count);
        }

        if (1 == maxBase) return  n >= 1 ? 1 : 0; // n是个位数,直接返回

        return count(String.valueOf(n), maxBase); // 将n转化成字符串计算
    }

    /*
        nStr:传入的数字字符串,保证第一个字符不是0
        maxBase:最高位对应的十进制位,如1,10,100 ...
     */
    int count(String nStr, int maxBase) {
        int maxBaseNum = nStr.charAt(0) - '0'; // 当前base最高位的值
        if (1 == maxBase) { // 计算到个位, 无需再计算next base
            return maxBaseNum >= 1 ? 1 : 0;
        }

        int nextBase = maxBase / 10;
        int cnt = dp.get(nextBase); // 先计算上一个base的个数,如当前base是1000,则上一个base是100,且表示0 ~ 999之间的1的个数
        String nextStr = nStr.substring(1); // 最高位之后的数

        if (1 == maxBaseNum) { // 最高位是1,则当前1出现的次数为 0 ~ nextStr, 即nextStr + 1
            cnt += Integer.valueOf(nextStr) + 1;
        } else if (maxBaseNum > 1) { // 最高位大于1,例:最高位是4, 当且maxBase是1000, 则1000 ~ 1999共1000个1, 1000 ~ 1999/3000 ~ 3999共三个0 ~ 999
            cnt = cnt * maxBaseNum + maxBase; // 加上next base的,共4个0 ~ 999,即cnt的值
        } else {
            // maxBaseNum < 1, 不存在的情况
        }

        // 递归计算最高位后边的情况, 最高位后边有0, 则直接跳过
        int i = 0;
        while (i < nextStr.length() && '0' == nextStr.charAt(i)) {
            i ++;
            nextBase /= 10;
        }
        if (0 == nextBase) return cnt; // 最高位后边都是0, 无需递归计算

        // 递归计算后边不为0的情况
        cnt += count(nextStr.substring(i), nextBase);
        return cnt;
    }
}

全部评论

相关推荐

故事和酒66:假设一下,就算报了培训班,不还是要投简历,只是项目改了。那不如先写几个培训班的项目,纯靠编,然后试试有没有面试。如果真有再报也不迟,如果没有还是没有,那就不是培训班的问题了。
点赞 评论 收藏
分享
星期一的大老师:项目描述 和 技术栈单开一栏;八股文:算法与数据结构,计算机网络一定要写,操作系统不了解可以不写;Linux命令,Git,Docker基础命令和基本使用一定要写,要有实际使用场景的解决经验;项目的八股文上:redis 解决 缓存雪崩,缓存击穿,缓存穿透的解决方案,一个问题的不同方案可以一起用,不需要重复在两个项目写。第二个项目换一个。小厂可以投一投
投了多少份简历才上岸
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务