【剑指offer-C++】JZ43:整数中1出现的次数(从1到n整数中1出现的次数)

【剑指offer-C++】JZ43:整数中1出现的次数-从1到n整数中1出现的次数

    • 题目描述
    • 解题思路

题目描述

描述:输入一个整数 n ,求 1~n 这 n 个整数的十进制表示中 1 出现的次数
例如, 1~13 中包含 1 的数字有 1 、 10 、 11 、 12 、 13 因此共出现 6 次。

注意:11 这种情况算两次。

数据范围: 1≤n≤30000 。

进阶:空间复杂度 O(1) ,时间复杂度 O(lognn) 。

输入:13
返回值:6
输入:0
返回值:0

解题思路

整数中1出现的次数(从1到n整数中1出现的次数):最直观的想法是,遍历1到n,然后将每一个数转换为字符串,再遍历字符串求字符1出现的次数,依次累加即可得到结果。其中,之所以将数字转换为字符串,是为了利用字符串的长度函数来求长度,避免整数位数不确定的情况。

int NumberOf1Between1AndN_Solution(int n) 
{
    string str;
    int res=0;
    for(int i=1;i<=n;i++)
    {
        str=to_string(i);
        for(int j=0;j<str.size();j++)
        {
           if(str[j]=='1')
              res++;
        }
     }
     return res;
}

优化:数位dp,即按位统计当前位为1的个数。使用一个变量base表示当前位是个位、十位、百位还是更高位,初始base等于1即表示个位,使用一个变量res表示1的个数。首先将数字n分为三个部分,分别是当前位cur,其计算方式为cur=n/base%10,高位a,其计算方式为a=n/base/10,低位b,其计算方式为b=n%base。其中计算当前位cur为1的情况下数字的个数一共分为三种情况:第一种是cur=0的情况,如果要使得当前位为1,那必须向前借位,则高位范围为0~(a-1),低位范围为0~(m个9),其中m对应base的位数,比如base等于1则表示个位,于是低位范围为0,个数为1,比如base等于10则表示十位,于是低位范围为0~9,个数为10,比如base等于100则表示百位,于是低位范围为0~99,个数为100,以此类推,于是当前位为1的数字个数总共为a*base个;第二种是cur=1的情况,即当前位已经为1,则分为两种情况,一是高位为a,则低位范围是0~b,一共有1*(b+1)个,二是高位范围为0~(a-1),低位范围为0~(m个9),这种情况同上,一共有a*base个,则总共为a*base+(b+1);第三种是cur=2~9的情况,则使得当前位为1,高位范围为0~a,低位范围为0~(m个9),总共为(a+1)*base个。循环条件是base小于等于n,具体代码以及注释如下。

int NumberOf1Between1AndN_Solution(int n) 
{
    int res=0;  //表示1的个数
    int base=1; //表示当前位
    int cur; //表示当前位
    int a;   //表示高位
    int b;   //表示低位
    //base不能超过n 等于是可以的
    while(base<=n)
    {
       cur=n/base%10;
       a=n/base/10;
       b=n%base;
       //cur=0 借位使得当前为1 则高位范围为0~(a-1) 低位为0~9(m个9) m对应base的位数 总共为a*base
       if(cur==0)
          res+=a*base;
       //cur=1 当前位为1 则分为两部分 一种情况是高位范围为0~(a-1) 低位为0~9(m个9) m对应base的位数 总共为a*base 另一种情况是高位为a 低位为0~b 总共为b+1
       else if(cur==1)
          res+=a*base+(b+1);
       //cur=2~9 使得当前为1 则高位范围为0~a 低位为0~9(m个9) m对应base的位数 总共为(a+1)*base
       else
          res+=(a+1)*base;
       //向前进一位
       base*=10; 
     }
     return res;
}

你可能感兴趣的:(剑指offer,c++,算法,动态规划)