ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

力扣233(java)-数字1的个数(困难)

2022-08-14 13:02:40  阅读:182  来源: 互联网

标签:10 java int 个数 力扣 99 100 dp 233


题目:

给定一个整数 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 }

标签:10,java,int,个数,力扣,99,100,dp,233
来源: https://www.cnblogs.com/liu-myu/p/16582849.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有