📌  相关文章
📜  范围内的数字计数,其中数字包含的位数不超过K个非零数字

📅  最后修改于: 2021-04-26 10:54:06             🧑  作者: Mango

给定一个范围,该范围由两个正整数L和R以及一个正整数K表示。请在该范围内所包含的位数不超过K个非零数字的范围内查找数字的计数。

例子:

Input : L = 1, R = 1000, K = 3
Output : 1000
Explanation : All the numbers from 1 to 1000 
are 3 digit numbers which obviously cannot 
contain more than 3 non zero digits.

Input : L = 9995, R = 10005
Output : 6
Explanation : Required numbers are 
10000, 10001, 10002, 10003, 10004 and 10005

先决条件:Digit DP

有两种方法可以解决此类问题,一种可以是组合解决方案,另一种可以是基于动态编程的解决方案。下面是使用数字动态编程方法解决此问题的详细方法。
动态编程解决方案:首先,如果我们能够计算直到R的所需数字,即在[0,R]范围内,则可以通过求解从零到R,然后在[L,R]范围内轻松得出答案。减去从零到L – 1求解后得到的答案。现在,我们需要定义DP状态。
DP州

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

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

下面是上述方法的实现。

C++
// CPP Program to find the count of
// numbers in a range where the number
// does not contain more than K non
// zero digits
  
#include 
using namespace std;
  
const int M = 20;
  
// states - position, count, tight
int dp[M][M][2];
  
// K is the number of non zero digits
int K;
  
// This function returns the count of
// required numbers from 0 to num
int countInRangeUtil(int pos, int cnt, int tight,
                     vector num)
{
    // Last position
    if (pos == num.size()) {
        // If count of non zero digits
        // is less than or equal to K
        if (cnt <= K)
            return 1;
        return 0;
    }
  
    // If this result is already computed
    // simply return it
    if (dp[pos][cnt][tight] != -1)
        return dp[pos][cnt][tight];
  
    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;
  
        // If the current digit is nonzero
        // increment currCnt
        if (dig != 0)
            currCnt++;
  
        int currTight = tight;
  
        // At this position, number becomes
        // smaller
        if (dig < num[pos])
            currTight = 1;
  
        // Next recursive call
        ans += countInRangeUtil(pos + 1, currCnt,
                                currTight, num);
    }
    return dp[pos][cnt][tight] = ans;
}
  
// This function converts a number into its
// digit vector and uses above function to compute
// the answer
int countInRange(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 countInRangeUtil(0, 0, 0, num);
}
  
// Driver Code to test above functions
int main()
{
    int L = 1, R = 1000;
    K = 3;
    cout << countInRange(R) - countInRange(L - 1) << endl;
  
    L = 9995, R = 10005, K = 2;
    cout << countInRange(R) - countInRange(L - 1) << endl;
    return 0;
}


Java
// Java Program to find the count of 
// numbers in a range where the number 
// does not contain more than K non 
// zero digits
import java.util.*;
class Solution
{
static final int M = 20; 
  
// states - position, count, tight 
static int dp[][][]= new int[M][M][2]; 
  
// K is the number of non zero digits 
static int K; 
static Vector num;
  
// This function returns the count of 
// required numbers from 0 to num 
static int countInRangeUtil(int pos, int cnt, int tight ) 
{ 
    // Last position 
    if (pos == num.size()) { 
        // If count of non zero digits 
        // is less than or equal to K 
        if (cnt <= K) 
            return 1; 
        return 0; 
    } 
  
    // If this result is already computed 
    // simply return it 
    if (dp[pos][cnt][tight] != -1) 
        return dp[pos][cnt][tight]; 
  
    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; 
  
        // If the current digit is nonzero 
        // increment currCnt 
        if (dig != 0) 
            currCnt++; 
  
        int currTight = tight; 
  
        // At this position, number becomes 
        // smaller 
        if (dig < num.get(pos)) 
            currTight = 1; 
  
        // Next recursive call 
        ans += countInRangeUtil(pos + 1, currCnt, currTight); 
    } 
    return dp[pos][cnt][tight] = ans; 
} 
  
// This function converts a number into its 
// digit vector and uses above function to compute 
// the answer 
static int countInRange(int x) 
{ 
    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 the number
# does not contain more than K non
# zero digits
  
# This function returns the count of
# required numbers from 0 to num
def countInRangeUtil(pos, cnt, tight, num):
  
    # Last position
    if pos == len(num):
  
        # If count of non zero digits
        # is less than or equal to K
        if cnt <= K:
            return 1
        return 0
  
    # If this result is already computed
    # simply return it
    if dp[pos][cnt][tight] != -1:
        return dp[pos][cnt][tight]
  
    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
  
        # If the current digit is nonzero
        # increment currCnt
        if dig != 0:
            currCnt += 1
  
        currTight = tight
  
        # At this position, number becomes
        # smaller
        if dig < num[pos]:
            currTight = 1
  
        # Next recursive call
        ans += countInRangeUtil(pos + 1, currCnt, currTight, num)
  
    dp[pos][cnt][tight] = ans
    return dp[pos][cnt][tight]
  
# This function converts a number into its
# digit vector and uses above function to compute
# the answer
def countInRange(x):
    global dp, K, M
  
    num = []
    while x:
        num.append(x % 10)
        x //= 10
  
    num.reverse()
  
    # Initialize dp
    dp = [[[-1, -1] for i in range(M)] for j in range(M)]
    return countInRangeUtil(0, 0, 0, num)
  
# Driver Code
if __name__ == "__main__":
  
    # states - position, count, tight
    dp = []
    M = 20
  
    # K is the number of non zero digits
    K = 0
  
    L = 1
    R = 1000
    K = 3
    print(countInRange(R) - countInRange(L - 1))
  
    L = 9995
    R = 10005
    K = 2
    print(countInRange(R) - countInRange(L - 1))
  
# This code is contributed by
# sanjeev2552


C#
// C# Program to find the count of 
// numbers in a range where the number 
// does not contain more than K non 
// zero digits
using System;
using System.Collections.Generic; 
  
class GFG
{
      
static int M = 20; 
  
// states - position, count, tight 
static int [,,]dp = new int[M, M, 2]; 
  
// K is the number of non zero digits 
static int K; 
static List num;
  
// This function returns the count of 
// required numbers from 0 to num 
static int countInRangeUtil(int pos,
                int cnt, int tight ) 
{ 
    // Last position 
    if (pos == num.Count) 
    { 
        // If count of non zero digits 
        // is less than or equal to K 
        if (cnt <= K) 
            return 1; 
        return 0; 
    } 
  
    // If this result is already computed 
    // simply return it 
    if (dp[pos, cnt, tight] != -1) 
        return dp[pos, cnt, tight]; 
  
    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; 
  
        // If the current digit is nonzero 
        // increment currCnt 
        if (dig != 0) 
            currCnt++; 
  
        int currTight = tight; 
  
        // At this position, number becomes 
        // smaller 
        if (dig < num[pos]) 
            currTight = 1; 
  
        // Next recursive call 
        ans += countInRangeUtil(pos + 1, currCnt, currTight); 
    } 
    return dp[pos,cnt,tight] = ans; 
} 
  
// This function converts a number into its 
// digit vector and uses above function to compute 
// the answer 
static int countInRange(int x) 
{ 
    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++)
            dp[i, j, k] = -1;
    return countInRangeUtil(0, 0, 0); 
} 
  
// Driver Code 
public static void Main()
{ 
    int L = 1, R = 1000; 
    K = 3; 
    Console.WriteLine( countInRange(R) - countInRange(L - 1) ); 
  
    L = 9995; R = 10005; K = 2; 
    Console.WriteLine( countInRange(R) - countInRange(L - 1) ); 
}
} 
  
/* This code contributed by PrinciRaj1992 */


输出:
1000
6

时间复杂度: O(18 * 18 * 2 * 10),如果我们要处理的数字最大为10 18