题解 | 整数中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;
    }
}

全部评论

相关推荐

09-30 11:52
门头沟学院 Java
点赞 评论 收藏
分享
09-17 10:53
四川大学 C++
牛客91242815...:会写标书没有任何卵用,鉴定为横向垃圾导师的受害者
点赞 评论 收藏
分享
头像
10-22 20:13
中南大学 Java
序言大家好呀。我是希晨er,一个初入职场的程序猿小登最近上班摸鱼刷到了一篇文章:10年深漂,放弃高薪,回长沙一年有感,还有聊聊30岁大龄程序员过往的心路历程,突然就有点感慨。我如今也做出了和大明哥一样的抉择,只是更早。此外我22年的人生,好像从来没好好记录过。正好现在工作不太忙,就想把这些经历写下来,也希望能得到社区里各位前辈的指点个人背景我是03年出生的西安娃,父母都是普通打工人。刚从中南大学软件工程专业毕业半年,现在在老家的央企过着躺平摆烂的日子成长轨迹从农村到城市的童年我家并不是西安的,只是爸妈在西安上班,从小学之后就把我接到了西安。后来老家房子拆了,爷爷奶奶也搬了过来。农村的生活,我觉...
Yki_:看哭了,恋爱那一段你女朋友说你不够关心她,可你毕竟也愿意遇到矛盾写几千字来和她慢慢分析;说不愿意给她花钱,我感觉可能只是消费观不一样;如果她想留在长沙,也应该提前跟你说开。不过她也许会心疼你放弃大厂offer转向数字马力?我也因为同样的原因有过一段幸福而充满遗憾的感情,不过跟爱情相比确实前途更重要一点。至于offer的选择,换我我也会这么选。把这些旧事记录下来以后,接下来就好好向前看吧,加油兄弟
🍊晨光随笔
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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