剑指offer31_整数中1出现的次数_题解

整数中1出现的次数

题目描述

求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

示例1

输入

13

返回值

6

分析

方案一:数学规律

\(1-n\) 的个位、十位、百位、...的 \(1\) 出现次数相加,即为 \(1\) 出现的总次数。

某位中 \(1\) 出现次数的计算方法:

\[出现次数=high*digit\tag{cur=0} \]

\[出现次数=high*digit+low+1\tag{cur=1} \]

\[出现次数=(high+1)*digit\tag{cur>1} \]

变量递推公式:
设计按照 “个位、十位、...” 的顺序计算,则 \(high、cur、low、digit\) 应初始化为:

high = n // 10
cur = n % 10
low = 0
digit = 1 # 个位

因此,从个位到最高位的变量递推公式为:

while high != 0 or cur != 0: # 当 high 和 cur 同时为 0 时,说明已经越过最高位,因此跳出
   low += cur * digit # 将 cur 加入 low ,组成下轮 low
   cur = high % 10 # 下轮 cur 是本轮 high 的最低位
   high //= 10 # 将本轮 high 最低位删除,得到下轮 high
   digit *= 10 # 位因子每轮 × 10

参考题解链接

https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/mian-shi-ti-43-1n-zheng-shu-zhong-1-chu-xian-de-2/

代码

/**
时间复杂度:O(logn)
循环内的计算操作使用 O(1) 时间;循环次数为数字 n 的位数,即 log10n ,因此循环使用 O(logn) 时间。
空间复杂度:O(1)
**/
class Solution
{
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int digit = 1, ans = 0;
        int high = n / 10, cur = n % 10, low = 0;
        // 当 high 和 cur 同时为 0 时,说明已经越过最高位,因此跳出
        while (high != 0 || cur != 0)
        {
            if (cur == 0)
                ans += high * digit;
            else if (cur == 1)
                ans += high * digit + low + 1;
            else
                ans += (high + 1) * digit;
            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        return ans;
    }
};

方案二:数位DP

代码

/**
时间复杂度:O(状态数*转移数)
状态数是dp数组的大小,转移数是for循环大小
空间复杂度:O(n2)
**/
class Solution
{
public:
    int a[11] = {0};
    int dp[11][11];
    int dfs(int pos, int cnt, int limit)
    {
        if (pos == 0)
        {
            return cnt;
        }
        if(!limit && dp[pos][cnt])
        {
            return dp[pos][cnt];
        }
        int res = 0;
        int up = (limit ? a[pos] : 9);
        for (int i = 0; i <= up; ++i)
        {
            res += dfs(pos - 1, cnt + (i == 1), limit && (i == up));
        }
        if (!limit)
        {
            dp[pos][cnt] = res;
        }
        return res;
    }
    int NumberOf1Between1AndN_Solution(int n)
    {
        if (n <= 0)
            return 0;
        else
        {
            memset(dp, 0, sizeof(dp));
            int len = 0;
            while (n)
            {
                a[++len] = n % 10;
                n /= 10;
            }
            return dfs(len, 0, 1);
        }
    }
};
posted @ 2021-01-09 22:19  RiverCold  阅读(106)  评论(0编辑  收藏  举报