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

📅  最后修改于: 2021-09-17 16:04:06             🧑  作者: Mango

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

先决条件:数字DP

方法:首先,如果我们能够计算到 R 所需的数字,即在 [0, R] 范围内,我们可以通过从 0 到 R 求解,然后减去我们从 0 到 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


Javascript


输出:
17