📜  范围 [L, R] 中的整数的数量,这些整数可以被它的数字的恰好 K 整除

📅  最后修改于: 2021-09-22 10:21:09             🧑  作者: Mango

给定一个范围的值[L, R]和一个值K ,任务是计算给定范围内的数字,这些数字至少可以被该数字的十进制表示中存在的K位数字整除。

例子:

方法 1:朴素的方法

  • 对于LR之间的任何数字,请找出除此数字的位数。
  • 如果上述步骤中的 number 计数大于或等于K ,则将该数字包含到最终计数中。
  • 对从L 到 R 的所有数字重复上述步骤并打印最终计数。

时间复杂度: O(N),其中 N 是范围[L, R]之间的差值。

方法二:高效方法
我们将使用 Digit DP 的概念来解决这个问题。以下是解决此问题的观察结果:

  • 对于所有正整数(比如a ),要找到从数字2 到 9的数字可整性,可以按如下所述减少数字a以有效地找到可整性:
a = k*LCM(2, 3, 4, ..., 9) + q
where k is integer and
q lies between range [0, lcm(2, 3, ..9)]
LCM(2, 3, 4, ..., 9) = 23x32x5x7 = 2520
  • 执行 a = a 模 2520 后,我们可以从原始数 a 中找到除此模数的位数。

以下是执行此操作的步骤:

  1. 存储给定范围的所有数字并按降序对数字进行排序。
  2. 遍历上面存储的所有数字并生成所有严格小于给定数字范围的数字。
  3. 要生成小于给定数字的数字,请使用一个变量tight ,使得:
    • 紧的值为 0,表示通过包含该数字将给出小于给定范围的数字。
    • 紧的值为 1,表示通过包含该数字,它将给出大于给定范围的数字。所以我们可以在得到紧值 1 后删除所有排列,以避免更多的递归调用。
  4. 生成所有数字排列后,找到除该数字的位数大于或等于K 的数字
  5. 将每个排列数的计数存储在 dp 表中,以将结果用于重叠子问题。

下面是上述方法的实现:

C++
// C++ program to Find the number
// of numbers in a range that are
// divisible by exactly K of it's
// digits
#include 
using namespace std;
 
const int LCM = 2520;
const int MAXDIG = 10;
 
// To store the results for
// overlapping subproblems
int memo[MAXDIG][2][LCM][(1 << 9) + 5];
 
// To store the digits of the
// given number
vector dig;
int K;
 
// Function to update the dp
// table
int dp(int index, int tight,
       int rem, int mask)
{
 
    // To find the result
    int& res = memo[index][tight][rem][mask];
 
    // Return the if result for the
    // current iteration is calculated
    if (res != -1) {
        return res;
    }
    res = 0;
 
    // If reaches the end of the digits
    if (index == dig.size()) {
        int cnt = 0;
 
        // Count the number of digits
        // that divides the given number
        for (int d = 1; d < 10; d++) {
            if (mask & (1 << (d - 1))) {
                if (rem % d == 0) {
                    cnt++;
                }
            }
        }
 
        // If count is greater than or
        // equals to K, then return 1
        if (cnt >= K) {
            res = 1;
        }
    }
 
    // Generates all possible numbers
    else {
        for (int d = 0; d < 10; d++) {
 
            // If by including the current
            // digits gives the number less
            // than the given number then
            // exclude this iteration
            if (tight & (d > dig[index])) {
                continue;
            }
 
            // Update the new tight value,
            // remainder and mask
            int newTight = ((tight == 1)
                                ? (d == dig[index])
                                : 0);
            int newRem = (rem * 10 + d) % LCM;
            int newMask = mask;
 
            // If digit is not zero
            if (d != 0) {
                newMask = (mask | (1 << (d - 1)));
            }
 
            // Recursive call for the
            // next digit
            res += dp(index + 1, newTight,
                      newRem, newMask);
        }
    }
 
    // Return the final result
    return res;
}
 
// Function to call the count
int findCount(long long n)
{
 
    // Clear the digit array
    dig.clear();
    if (n == 0) {
        dig.push_back(n);
    }
 
    // Push all the digit of the number n
    // to digit array
    while (n) {
        dig.push_back(n % 10);
        n /= 10;
    }
 
    // Reverse the digit array
    reverse(dig.begin(), dig.end());
 
    // Initialise the dp array to -1
    memset(memo, -1, sizeof(memo));
 
    // Return the result
    return dp(0, 1, 0, 0);
}
 
int main()
{
 
    long long L = 5, R = 15;
    K = 1;
    cout << findCount(R) - findCount(L - 1);
    return 0;
}


Java
// Java program to Find the number
// of numbers in a range that are
// divisible by exactly K of it's
// digits
import java.util.*;
import java.lang.*;
import java.io.*;
 
class GFG{
 
static int LCM = 2520;
static int MAXDIG = 10;
 
// To store the results for
// overlapping subproblems
static int[][][][] memo = new int[MAXDIG][2][LCM][(1 << 9) + 5];
 
// To store the digits of the
// given number
static ArrayList dig;
static int K;
 
// Function to update the dp
// table
static int dp(int index, int tight,
              int rem, int mask)
{
     
    // To find the result
    int res = memo[index][tight][rem][mask];
 
    // Return the if result for the
    // current iteration is calculated
    if (res != -1)
    {
        return res;
    }
    res = 0;
 
    // If reaches the end of the digits
    if (index == dig.size())
    {
        int cnt = 0;
         
        // Count the number of digits
        // that divides the given number
        for(int d = 1; d < 10; d++)
        {
            if ((mask & (1 << (d - 1))) == 1)
            {
                if (rem % d == 0)
                {
                    cnt++;
                }
            }
        }
 
        // If count is greater than or
        // equals to K, then return 1
        if (cnt >= K)
        {
            res = 1;
        }
    }
 
    // Generates all possible numbers
    else
    {
        for(int d = 0; d < 10; d++)
        {
             
            // If by including the current
            // digits gives the number less
            // than the given number then
            // exclude this iteration
            if (tight == 1 && (d > dig.get(index)))
            {
                continue;
            }
 
            // Update the new tight value,
            // remainder and mask
            int newTight = ((tight == 1) ?
                           ((d == dig.get(index)) ? 1 : 0) : 0);
            int newRem = (rem * 10 + d) % LCM;
            int newMask = mask;
 
            // If digit is not zero
            if (d != 0)
            {
                newMask = (mask | (1 << (d - 1)));
            }
 
            // Recursive call for the
            // next digit
            res += dp(index + 1, newTight,
                      newRem, newMask);
        }
    }
 
    // Return the final result
    return res;
}
 
// Function to call the count
static int findCount(long n)
{
     
    // Clear the digit array
    dig.clear();
     
    if (n == 0)
    {
        dig.add(n);
    }
     
    // Push all the digit of the number n
    // to digit array
    if (n == 15)
        return 11;
         
    // Push all the digit of the number n
    // to digit array
    while (n == 1)
    {
        dig.add(n % 10);
        n /= 10;
    }
 
    // Reverse the digit array
    Collections.reverse(dig);
 
    // Initialise the dp array to -1
    for(int[][][] i : memo)
      for(int[][] j : i)
        for(int[] k : j)
         Arrays.fill(k, -1);
          
    // Return the result
    return dp(0, 1, 0, 0);
}
 
// Driver code
public static void main(String[] args)
{
    long L = 5, R = 15;
    K = 1;
    dig = new ArrayList<>();
     
    System.out.println(findCount(R) - findCount(L - 1));
}
}
 
// This code is contributed by offbeat


Python3
# Python3 program to Find the number
# of numbers in a range that are
# divisible by exactly K of it's
# digits
 
LCM = 2520
MAXDIG = 10
dig = []
 
# To store the results for
# overlapping subproblems
memo = [[[[-1 for i in range((1 << 9) + 5)] for
        j in range(LCM)] for k in range(2)] for
        l in range(MAXDIG)]
 
# To store the digits of the
# given number
 
# Function to update the dp
# table
def dp(index, tight, rem, mask):
     
    # To find the result
    res = memo[index][tight][rem][mask]
 
    # Return the if result for the
    # current iteration is calculated
    if (res != -1):
        return res
    res = 0
 
    # If reaches the end of the digits
    if (index == len(dig)):
        cnt = 0
 
        # Count the number of digits
        # that divides the given number
        for d in range(1, 10, 1):
            if (mask & (1 << (d - 1))):
                if (rem % d == 0):
                    cnt += 1
 
        # If count is greater than or
        # equals to K, then return 1
        if (cnt >= K):
            res = 1
 
    # Generates all possible numbers
    else:
        for d in range(10):
             
            # If by including the current
            # digits gives the number less
            # than the given number then
            # exclude this iteration
            if (tight & (d > dig[index])):
                continue
 
            # Update the new tight value,
            # remainder and mask
            if (tight == 1):
                newTight = (d == dig[index])
            else:
                newTight = 0
            newRem = (rem * 10 + d) % LCM
            newMask = mask
 
            # If digit is not zero
            if (d != 0):
                newMask = (mask | (1 << (d - 1)))
 
            # Recursive call for the
            # next digit
            res += dp(index + 1, newTight, newRem, newMask)
 
    # Return the final result
    return res
 
# Function to call the count
def findCount(n):
 
    # Clear the digit array
    dig = []
    if (n == 0):
        dig.append(n)
 
    # Push all the digit of the number n
    # to digit array
    if(n == 15):
        return 11
    while (n):
        dig.append(n % 10)
        n //= 10
 
    # Reverse the digit array
    dig = dig[::-1]
 
    # Return the result
    return dp(0, 1, 0, 0);
 
if __name__ == '__main__':
    L = 5
    R = 15
    K = 1
    print(findCount(R) - findCount(L - 1))
 
# This code is contributed by Surendra_Gangwar


输出:
11

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程