📌  相关文章
📜  数字d恰好出现K次的范围内的数字计数

📅  最后修改于: 2021-05-04 09:45:55             🧑  作者: Mango

给定两个代表范围的正整数LR以及另外两个正整数dK。任务是找到数字d恰好出现K次的范围内的数字计数。

例子:

先决条件:Digit DP

方法:首先,如果我们能够计算所需的数字,直到R,即在[0,R]范围内,则可以通过求解从零到R的值,然后减去该值,就可以轻松地在[L,R]范围内得出答案。从零到L – 1求解后得到的答案。现在,我们需要定义DP状态。
DP州

  • 既然我们可以考虑我们的号码作为数字序列,一个状态是,我们目前的位置,这个位置可以有值从0到18,如果我们处理的是数字高达10 18。在每个递归调用中,我们尝试通过从0到9放置一个数字来从左到右构建序列。
  • 第二种状态是计数,它定义了次数,到目前为止,我们已经将数字d放置了。
  • 另一个状态是布尔变量tight ,它指示我们要构建的数字已经小于R,因此在接下来的递归调用中,我们可以将0到9之间的任何数字放置。如果数字没有变小,则最大限制为我们可以放置的数字是R中当前位置的数字。
  • 最后一个状态也是布尔变量nonz ,如果正在构建的数字中有任何前导零,则有助于考虑这种情况,我们不需要对它们进行计数。

    在最后的递归调用中,如果我们在最后一个位置,如果数字d的计数等于K,则返回1,否则返回0。

    下面是上述方法的实现:

    C++
    // CPP Program to find the count of
    // numbers in a range where digit d
    // occurs exactly K times
    #include 
    using namespace std;
      
    const int M = 20;
      
    // states - position, count, tight, nonz
    int dp[M][M][2][2];
      
    // d is required digit and K is occurrence
    int d, K;
      
    // This function returns the count of
    // required numbers from 0 to num
    int count(int pos, int cnt, int tight,
              int nonz, vector num)
    {
        // Last position
        if (pos == num.size()) {
            if (cnt == K)
                return 1;
            return 0;
        }
      
        // If this result is already computed
        // simply return it
        if (dp[pos][cnt][tight][nonz] != -1)
            return dp[pos][cnt][tight][nonz];
      
        int ans = 0;
      
        // Maximum limit upto which we can place
        // digit. If tight is 1, means number has
        // already become smaller so we can place
        // any digit, otherwise num[pos]
        int limit = (tight ? 9 : num[pos]);
      
        for (int dig = 0; dig <= limit; dig++) {
            int currCnt = cnt;
      
            // Nonz is true if we placed a non
            // zero digit at the starting of
            // the number
            if (dig == d) {
                if (d != 0 || (!d && nonz))
                    currCnt++;
            }
      
            int currTight = tight;
      
            // At this position, number becomes
            // smaller
            if (dig < num[pos])
                currTight = 1;
      
            // Next recursive call, also set nonz
            // to 1 if current digit is non zero
            ans += count(pos + 1, currCnt,
                         currTight, nonz || (dig != 0), num);
        }
        return dp[pos][cnt][tight][nonz] = ans;
    }
      
    // Function to convert x into its digit vector and uses
    // count() function to return the required count
    int solve(int x)
    {
        vector num;
        while (x) {
            num.push_back(x % 10);
            x /= 10;
        }
        reverse(num.begin(), num.end());
      
        // Initialize dp
        memset(dp, -1, sizeof(dp));
        return count(0, 0, 0, 0, num);
    }
      
    // Driver Code to test above functions
    int main()
    {
        int L = 11, R = 100;
        d = 2, K = 1;
        cout << solve(R) - solve(L - 1) << endl;
      
        return 0;
    }


    Java
    // Java Program to find the count of
    // numbers in a range where digit d
    // occurs exactly K times
    import java.util.*;
    class Solution
    {
    static final int M = 20;
       
    // states - position, count, tight, nonz
    static int dp[][][][]= new int[M][M][2][2];
       
    // d is required digit and K is occurrence
    static int d, K;
       
    // This function returns the count of
    // required numbers from 0 to num
    static int count(int pos, int cnt, int tight,
              int nonz, Vector num)
    {
        // Last position
        if (pos == num.size()) {
            if (cnt == K)
                return 1;
            return 0;
        }
       
        // If this result is already computed
        // simply return it
        if (dp[pos][cnt][tight][nonz] != -1)
            return dp[pos][cnt][tight][nonz];
       
        int ans = 0;
       
        // Maximum limit upto which we can place
        // digit. If tight is 1, means number has
        // already become smaller so we can place
        // any digit, otherwise num[pos]
        int limit = ((tight !=0)? 9 : num.get(pos));
       
        for (int dig = 0; dig <= limit; dig++) {
            int currCnt = cnt;
       
            // Nonz is true if we placed a non
            // zero digit at the starting of
            // the number
            if (dig == d) {
                if (d != 0 || (d==0 && nonz!=0))
                    currCnt++;
            }
       
            int currTight = tight;
       
            // At this position, number becomes
            // smaller
            if (dig < num.get(pos))
                currTight = 1;
       
            // Next recursive call, also set nonz
            // to 1 if current digit is non zero
            ans += count(pos + 1, currCnt,
                         currTight, (dig != 0?1:0), num);
        }
        return dp[pos][cnt][tight][nonz] = ans;
    }
       
    // Function to convert x into its digit vector and uses
    // count() function to return the required count
    static int solve(int x)
    {
        Vector num= new Vector();
        while (x!=0) {
            num.add(x % 10);
            x /= 10;
        }
        Collections.reverse(num);
       
        // Initialize dp
        for(int i=0;i


    Python3
    # Python Program to find the count of
    # numbers in a range where digit d
    # occurs exactly K times
    M = 20
      
    # states - position, count, tight, nonz
    dp = []
      
    # d is required digit and K is occurrence
    d, K = None, None
      
    # This function returns the count of
    # required numbers from 0 to num
    def count(pos, cnt, tight, nonz, num: list):
      
        # Last position
        if pos == len(num):
            if cnt == K:
                return 1
            return 0
      
        # If this result is already computed
        # simply return it
        if dp[pos][cnt][tight][nonz] != -1:
            return dp[pos][cnt][tight][nonz]
      
        ans = 0
      
        # Maximum limit upto which we can place
        # digit. If tight is 1, means number has
        # already become smaller so we can place
        # any digit, otherwise num[pos]
        limit = 9 if tight else num[pos]
      
        for dig in range(limit + 1):
            currCnt = cnt
      
            # Nonz is true if we placed a non
            # zero digit at the starting of
            # the number
            if dig == d:
                if d != 0 or not d and nonz:
                    currCnt += 1
      
            currTight = tight
      
            # At this position, number becomes
            # smaller
            if dig < num[pos]:
                currTight = 1
      
            # Next recursive call, also set nonz
            # to 1 if current digit is non zero
            ans += count(pos + 1, currCnt, 
                    currTight, (nonz or dig != 0), num)
      
        dp[pos][cnt][tight][nonz] = ans
        return dp[pos][cnt][tight][nonz]
      
      
    # Function to convert x into its digit vector and uses
    # count() function to return the required count
    def solve(x):
        global dp, K, d
      
        num = []
        while x:
            num.append(x % 10)
            x //= 10
      
        num.reverse()
      
        # Initialize dp
        dp = [[[[-1, -1] for i in range(2)] 
                for j in range(M)] for k in range(M)]
        return count(0, 0, 0, 0, num)
      
    # Driver Code
    if __name__ == "__main__":
      
        L = 11
        R = 100
        d = 2
        K = 1
        print(solve(R) - solve(L - 1))
      
    # This code is contributed by
    # sanjeev2552


    C#
    // C# Program to find the count of
    // numbers in a range where digit d
    // occurs exactly K times
    using System;
    using System.Collections.Generic; 
      
    class GFG
    {
        static readonly int M = 20;
      
        // states - position, count, tight, nonz
        static int [,,,]dp= new int[M, M, 2, 2];
      
        // d is required digit and K is occurrence
        static int d, K;
      
        // This function returns the count of
        // required numbers from 0 to num
        static int count(int pos, int cnt, int tight,
                int nonz, List num)
        {
            // Last position
            if (pos == num.Count) 
            {
                if (cnt == K)
                    return 1;
                return 0;
            }
      
            // If this result is already computed
            // simply return it
            if (dp[pos, cnt, tight, nonz] != -1)
                return dp[pos, cnt, tight, nonz];
      
            int ans = 0;
      
            // Maximum limit upto which we can place
            // digit. If tight is 1, means number has
            // already become smaller so we can place
            // any digit, otherwise num[pos]
            int limit = ((tight != 0) ? 9 : num[pos]);
      
            for (int dig = 0; dig <= limit; dig++)
            {
                int currCnt = cnt;
      
                // Nonz is true if we placed a non
                // zero digit at the starting of
                // the number
                if (dig == d) 
                {
                    if (d != 0 || (d == 0 && nonz != 0))
                        currCnt++;
                }
      
                int currTight = tight;
      
                // At this position, number becomes
                // smaller
                if (dig < num[pos])
                    currTight = 1;
      
                // Next recursive call, also set nonz
                // to 1 if current digit is non zero
                ans += count(pos + 1, currCnt,
                            currTight, (dig != 0 ? 1 : 0), num);
            }
            return dp[pos, cnt, tight, nonz] = ans;
        }
      
        // Function to convert x into its 
        // digit vector and uses count() 
        // function to return the required count
        static int solve(int x)
        {
            List num = new List();
            while (x != 0) 
            {
                num.Add(x % 10);
                x /= 10;
            }
            num.Reverse();
      
            // Initialize dp
            for(int i = 0; i < M; i++)
                for(int j = 0; j < M; j++)
                    for(int k = 0; k < 2; k++)
                        for(int l = 0; l < 2; l++)
                            dp[i, j, k, l]=-1;
      
            return count(0, 0, 0, 0, num);
        }
      
        // Driver Code 
        public static void Main()
        {
            int L = 11, R = 100;
            d = 2; K = 1;
            Console.Write( solve(R) - solve(L - 1) );
        }
    }
      
    // This code is contributed by Rajput-JI


    输出:
    17