题目:

给定一个整数 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. MK5 机械键盘 说明书

    FN + 右箭头 就是加快节奏 FN + ScrLk 就是切换模式

  2. c 的头文件标准格式

    前记: C语言的头文件是嵌入式系统中常用的,也是很多人没有注意的,但是写的很差的,这里给出一个经典的模板,仅供参考. 正文: 经典的格式: /***************************** ...

  3. Android开发过程中的坑及解决方法收录(7)

    1.无法找到自定义View的实例对象 问题描述: 由于经常要使用到两个TextView的布局,所以,我打算将其封装成一个自定义View,封装成功,界面能够成功显示了,但是,想给它设置点击监听器的时候就 ...

  4. [模板]01trie,维护异或最大值

    // 查询异或最大值,每次插入和查询时间都是log(C) template<class T> class trie01 { vector<vector<T>> tr ...

  5. AI 学习时代:大语言模型领域的行业黑话和专业术语解析

    近年来,深度学习技术的快速发展带动了大语言模型在自然语言处理领域的广泛应用.在这个激动人心的领域里,我们常常会遇到一些行业黑话和专业术语.为了帮助大家更好地入门,让我们深入探讨一些关键概念,以及它们在 ...

  6. 瑞云科技CTO赵志杰出席广州广告数字创意峰会并发表演讲

    3月23日下午,广州广告数字创意峰会暨穗广协企业家大讲堂年度巡礼活动在广州图书馆圆满举行.本次峰会由广州市人民政府统筹,中共广州市委宣传部.广州市文化广电旅游局.中共广州市天河区委.广州市天河区人民政 ...

  7. 从 Linux 内核角度探秘 JDK MappedByteBuffer

    本文涉及到的内核源码版本为: 5.4 ,JVM 源码为:OpenJDK17,RocketMQ 源码版本为:5.1.1 在之前的文章<一步一图带你深入剖析 JDK NIO ByteBuffer 在 ...

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

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

  9. 记录--UNI-APP安卓本地打包详细教程(保姆级)

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.开发环境 uni-app 官方文档地址 原生开发者支持 1.Android Studio 下载地址:Android Studio官网 ...

  10. 灰狼优化算法(MOGWO)

    灰狼优化算法(MOGWO) 摘要 固定大小的外部档案用来保存帕累托优化解 在多目标搜索空间中,这个档案被用来定义狼群社会等级和捕猎行为 这个算法在10个多目标测试集进行测试,并与MOEA/D和MOPS ...