The sword refers to the number of times 1 in the integer of 1~n appears in Offer 43.

The sword refers to the number of times 1 in the integer of 1~n appears in Offer 43.

Enter an integer n and find the number of occurrences of 1 in the decimal representation of n integers from 1 to n.
For example, if you enter 12, the numbers containing 1 in the integers from 1 to 12 are 1, 10, 11, and 12, and 1 appears 5 times in total.

Source: LeetCode
Link: https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof
Copyright is owned by LeetCode . For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Reprinted from my LeetCode explanations

This is a typical digital dynamic programming problem . After mastering the ideas and templates of digital dynamic programming, you can kill it in seconds.
First, the solution of using brute force enumeration and splitting all numbers timed out . Therefore, the algorithm can be updated from the perspective of improving enumeration.
Take 12345 as an example. To enumerate such a large number, you can do this: starting from the highest bit, when the highest bit is 0 (that is, the highest bit does not reach its upper bound 1), the next digit can be taken from 0 to 9 When the highest digit is 1 (that is, the highest digit reaches its upper bound 1), the next digit can only take a number from 0 to 2 (because 2 is the upper bound of this number). By analogy, all numbers can be enumerated. In this way, we understand the first step of digital dynamic programming, which is to enumerate numbers violently . Obviously, in the enumeration process, we have to mark whether the previous digit has reached the upper bound to determine the enumeration range of the current digit.
This has a very core advantage, that is, it is essentially a bitwise enumeration , which saves the original process of splitting numbers, that is, as long as a certain bit is enumerated to 1, we will add +1 to the total.
Furthermore, the simple recursive writing method will time out, because many calculations are repeated, so in the recursive process, the result of each time is recorded and used directly when needed. Space for time. Therefore, an array is needed to store the intermediate results. It is important to understand that the array is to store the result of the corresponding recursion, and the recursive function has parameters, so the parameter is used as the subscript of the array to mark the result of this recursion.
The detailed code is as follows:

class Solution {
    
    
    private int[][][] dp = null;
    private int[] upperBound = null;

    public int countDigitOne(int n) {
    
    
        String string = String.valueOf(n);
        char[] chars = string.toCharArray();//实现对n的从最高位到最低位数位拆分
        int length = chars.length;
        upperBound = new int[length];//upperBound数组记录从n的最低位到最高位,每一位数字的上限
        for(int i = 0; i < length; ++i) {
    
    
            upperBound[i] = chars[length - 1 - i] - '0';//初始化upperBound数组,注意下标的变换,因为记录的是从n的最低位到最高位的数字上限
        }       
        dp = new int[length][2][length];//int[pos][limit][sum],下标对应dfs的参数,标识、记录中间结果
        for(int i = 0; i < length; ++i) {
    
    
            for(int j = 0; j < 2; ++j) {
    
    
                Arrays.fill(dp[i][j], -1);//初始化dp数组,用-1表示对应的dfs有没有执行过
            }
        }
        return this.dfs(length - 1, 1, 0);
    }

    private int dfs(int pos, int limit, int sum) {
    
    //pos与upperBound的下标对应
        if(pos == -1) return sum;//递归结束,返回sum
        if(dp[pos][limit][sum] != -1) return dp[pos][limit][sum];//对应的dfs执行过了,就直接返回以前算过的值,空间换时间,节省时间开销
        int maxNum = (limit == 1) ? upperBound[pos] : 9;//当前第pos位的数有上界要求,取上界数字,否则取9
        int res = 0;
        for(int i = 0; i <= maxNum; ++i) {
    
    //对当前第pos位的数字进行枚举,范围从0到maxNum
            //考虑低一位的数字:当当前第pos位有上界且达到了上界,低一位数字才有上界;
            //当当前第pos位为1时,1出现的次数加1,因此sum需要加1
            res += dfs(pos - 1, ((limit == 1) && (i == maxNum)) ? 1 : 0, (i == 1) ? sum + 1 : sum);
        }
        return dp[pos][limit][sum] = res;//标识、记录res,并返回res
    }
}

Guess you like

Origin blog.csdn.net/m0_55570281/article/details/114156236