题目:

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

示例 1:

输入:n = 13
输出:6
示例 2:

输入:n = 0
输出:0

提示:

0 <= n <= 109

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/number-of-digit-one
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

这个题是真的很打脑壳啊~参考【 liweiwei1419】和 【2021293707L2】的题解,谢谢大佬!

主要运用数位dp的思路,采用【自底向上】的递推求解,实际是【动态规划】关于数位的入门问题。

题目的意思是:在1~n 的所有整数中出现1的个数,不是整数的个数,而是整数中包含的1的个数!

例如:15

包含1的整数有:1, 10, 11, 12, 13, 14, 15, 一共有 1 + 1 + 2  + 1 + 1 + 1 + 1 = 8 ,所以1~15的所有整数中一共包含8个1。

数位:是把一个数按照个位、十位、百位拆开来看。因此本题就可以拆分为:个位出现1的个数、十位出现1的个数、百位出现1的个数...

定义状态1:A[i] 表示0~10i+1-1的所有i+1位数里1的总数。

例如:

  • 0~9 所有的 1 位数里的1的总数;
  • 0~99 所有的 2 位数里的1的总数;
  • 0~999所有的 3 位数里的1的总数;
  • ......
  • 0~999999所有的 6 位数里的1的总数;

  • A[0] =1,0~9所有的1位数里含有的1的个数只有1个;
  • A[1]考虑的范围为0~99所有两位数中含有的1的个数,分类讨论:
    • 个位数固定为1( _1 ),十位数可能为0~9(01,11,21,31,41,51,61,71,81,91),一共10种可能,十位为1这个1先不统计,这种情况下的结果是 10 * A[0];
    • 十位数固定为1,个位数为0~9(10,11,12,13,14,15,16,17,18,19),一共10种可能,这时候把十位为1这个1统计到了,这种情况下的结果是10种。因此A[1]= 10 * A[0] +101
  • A[2]考虑的范围为0~999所有的三位数中含有的1的个数,分类讨论:
    • 最高位是0,剩下的是0~99,即为A[1], 最高位是1,剩下的是0~99即为A[1],.....,最高位是9,剩下是A[1],这里也暂时不统计最高位的这个1,一共是 10*A[1];
    • 最高位固定为1,十位,个位为0~99,一共100个。因此A[2]= 10 * A[1] +102

根据上述规律,总结归纳出:A[i]= 10 * A[i -1 ] +10i

定义状态2:dp[i]表示:从右向左数截取到第 i + 1位到最后一个数组成的数字里面包含1的总个数。

例如:2875

  • dp[0]表示截取到1位数4中(1~4)所有数里1的总数;
  • dp[1]表示截取到2位数75中(1~75)所有数里1的总数;
  • dp[2]表示截取到3位数875中(1~875)所有数里1的总数;
  • dp[3]表示截取到4位数2875中(1~2875)所有数里1的总数;

因此从右向左遍历 给定整数n的每一位 i ,假设当前遍历到的数是m(0 <= m <= 9),根据遍历到的数值进行分类讨论:

dp[0]代表的是个位数包含的1的个数,如果m =0,表示个位数为0即dp[0] == 0,当m==0~9时,这10个数只有一个1即dp[0] == 1

  • 当m == 0时,此时的状态值取决于m右边一位的状态值,dp[ i ] = dp[ i-1 ];
  • 当m == 1时,例如166:
    • 首先:求出0~66中元素1的个数,即 dp[ i-1 ]
    • 其次:求出100~166中元素1的个数,最高位是1,只需要看他右边的数,即共有67个(100、101、......、166)
    • 最后:求出0~99中元素1的个数即为A[i-1](注意:这里我还想过会和首先:0~66重合,其实不会,因为166分为三批次:0~99小于100的元素1的个数,100~166首位为1的三位数,0~66这里是算100以上的数中1的个数)。
  • 当m > 1时,例如465:
    • 首先:最高位不是1,看它后一位的状态值即dp[i-1],0~65中元素1的个数;
    • 其次:最高位是1,数出最高位是1的元素的个数即 10i,100~199中元素1的个数;
    • 最后:算其他模块中包含1的个数即m* A[i-1],465中0~99,100~199(与其次不是同一种),200~299,300~399各模块中包含的1的个数,即4 * A[1]。

代码:

 1 class Solution {
2 public int countDigitOne(int n) {
3 //将整数转换成字符串
4 String s = String.valueOf(n);
5 char[] ca = s.toCharArray();
6 int m = s.length();
7 //如果只有个位数则只包含一个1
8 if(m == 1){
9 return n == 0 ? 0 : 1;
10 }
11 //求状态1:A[i]: 0~10^(i+1) -1包含1的个数
12 //0~9,0~99,0~999
13 //如果是5位数,只需要求0~9999中出现1的个数
14 int[] A = new int[m-1];
15 // 0~9
16 A[0] = 1;
17 for(int i = 1; i < m-1; i++){
18 A[i] = 10 * A[i-1] + (int) Math.pow(10, i);
19 }
20 //求状态2:dp[i]
21 int[] dp = new int[m];
22 //如果最后一位为0
23 if(ca[m - 1] == '0'){
24 dp[0] = 0;
25 }else{
26 //最后一位不为0,即为0~9
27 dp[0] = 1;
28 }
29 for(int i = 1; i < m; i++){
30 //从右向左截取每一数位
31 char currChar = ca[m - i - 1];
32 if (currChar == '0'){
33 //当前位为0,状态值取决于它右边的1的个数
34 dp[i] = dp[i - 1];
35 } else if (currChar == '1'){
36 //最高位为1,分为三个步骤相加
37 //例如166,其次:求出100~166中元素1的个数
38 int res = Integer.parseInt(s.substring(m - i, m)) + 1;
39 //166中0~66,和小于100d 0~99中1的个数
40 dp[i] = res + dp[i-1] + A[i-1];
41 }else{
42 //最高位大于1的情况,例如465
43 //最高位不是1个数,dp[i-1]
44 //最高位是1,即为10^i
45 //算其他模块:m * A[i-1]
46 dp[i] = (currChar - '0') * A[i-1] + dp[i-1] + (int) Math.pow(10,i);
47 }
48 }
49 return dp[m - 1];
50 }
51 }

力扣233(java)-数字1的个数(困难)的更多相关文章

  1. 力扣485. 最大连续1的个数-C语言实现-简单题

    题目 [题目传送门] 给定一个二进制数组, 计算其中最大连续1的个数. 示例 1: 输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3 ...

  2. Java实现 LeetCode 233 数字 1 的个数

    233. 数字 1 的个数 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数. 示例: 输入: 13 输出: 6 解释: 数字 1 出现在以下数字中: 1, 10, 11, 1 ...

  3. 力扣Leetcode 面试题56 - I. 数组中数字出现的次数

    面试题56 - I. 数组中数字出现的次数 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 示例 ...

  4. 刷题-力扣-剑指 Offer 15. 二进制中1的个数

    剑指 Offer 15. 二进制中1的个数 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de- ...

  5. Leetcode 233.数字1的个数

    数字1的个数 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数. 示例: 输入: 13 输出: 6 解释: 数字 1 出现在以下数字中: 1, 10, 11, 12, 13 . ...

  6. 力扣——single number (只出现一次的数字) python实现

    题目描述: 中文: 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 英 ...

  7. 力扣——single number 2(只出现一次的数字 2) python实现

    题目描述: 中文: 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? ...

  8. 力扣算法经典第一题——两数之和(Java两种方式实现)

    一.题目 难度:简单 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数, 并返回它们的数组下标. 你可以假设每种输入只会对应一 ...

  9. 力扣Leetcode 179. 最大数 EOJ 和你在一起 字符串拼接 组成最大数

    最大数 力扣 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数. 示例 1: 输入: [10,2] 输出: 210 示例 2: 输入: [3,30,34,5,9] 输出: 9534330 说 ...

  10. 力扣567.字符串的排列—C语言实现

    题目 来源:力扣(LeetCode)

随机推荐

  1. 投屏项目中Sink端CPU占用过高问题

    一.背景 今天来总结一下,自己在项目中遇到的一个CPU占用过高的问题,详细的结束从发现到定位在到解决问题的过程. 原因是性能测试那边提出了一个bug,就是在投屏过程中,平板端也就是Sink端功耗非常高 ...

  2. uni-app开发经验分享二十二: uni-app大转盘思路解析

    最近在研究uni-app,制作了一个很有意思的集合项目demo,这里给大家分享下大转盘的前端设计思路 需求案例:大转盘抽奖 线上demo查看: 案例效果: 制作思路: 前端大转盘使用css3动画来做, ...

  3. 记录--你知道Vue中的Scoped css原理么?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 追忆Scoped 偶然想起了一次面试,二面整体都聊完了,该做的算法题都做出来了,该背的八股文也背的差不多了,面试官频频点头,似乎对我的基础 ...

  4. .NET Emit 入门教程:第四部分:构建类型(Type)

    前言: 在动态生成代码的过程中,构建类型(Type)是至关重要的一步. 通过使用 Emit 中的 TypeBuilder,我们可以定义和创建各种类型,包括类.结构体和接口. 本节将深入探讨如何使用 T ...

  5. 使用 NocoDB 一键将各种数据库转换为智能表格

    NocoDB 是一款开源的无代码数据库平台,可以进行数据管理和应用开发.它的灵感来自 Airtable,支持与 Airtable 类似的电子表格式交互.关系型数据库 Schema 设计.API 自动生 ...

  6. llama2+localGPT打造纯私有知识助手

    通过部署llama2系列,可以构建本地私有的知识小助手 用来输出一写周报.月报,甚至辅助数据分析都可以(想想都很轻松) 想要大模型支持特定的数据集,就需要进行专业的fine-turing 但是fine ...

  7. Redis redis-cli 你需要知道这些有用的命令

    一.--stat 输出当前 redis 服务节点状态 命令:redis-cli -h host -p port --stat 输出: 连续输出,默认interval 1s 键数 | 内存 | 客户端数 ...

  8. 强!10.6K star,一款开源HTTP测试工具,适合新手,简单、容易上手!

    大家好,我是狂师! 今天给大家推荐一款开源的HTTP测试工具:Hurl,相比curl.wget功能更强大,且更容易上手.很适用新手使用. 1.项目介绍 Hurl是一个使用Rust语言开发的命令行工具, ...

  9. Refresh 重构(Refactor)

    最近在闲暇之余重(第)温(一..次)此书, 首先能感受到的, 无论你是新程序员还是老程序员, 这本书都已经不具备太多的可读性了. 由于本书成书年代久远, 那个时候软件行业还不够发达, 面向对象还没有被 ...

  10. 7 HTML锚点应用

    7 锚点应用 锚点( anchor )是超链接的一种应用,也叫命名锚记,锚点可以像一个定位器一样,可以实现页面内的链接跳转,运用相当普遍.例如,我们有一个网页,由于内容太多,导致页面很长,而且里面的内 ...