给定一个值范围[L,R]和一个值K ,任务是计算给定范围内的数字,这些数字至少可以被该数字的十进制表示形式中存在的K个数字整除。
例子:
Input: L = 24, R = 25, K = 2
Output: 1
Explanation:
24 has two digits 2 and 4 and is divisible by both 2 and 4. So this satisfies the given condition.
25 has two digits 2 and 5 and is only divisible by 5. But since K = 2, it doesnot qualifies the mentioned criteria.
Input: L = 5, R = 15, K = 1
Output: 11
方法1:天真的方法
- 对于介于L到R之间的任何数字,请找到将其除以数字的位数。
- 如果以上步骤中的数字计数大于或等于K ,则将该数字包括到最终计数中。
- 对从L到R的所有数字重复上述步骤,并打印最终计数。
时间复杂度: O(N),其中N是范围[L,R]之间的差。
方法2:高效方法
我们将使用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 =模2520之后,我们可以从将该模除的原始数a中找到位数。
以下是执行此操作的步骤:
- 存储给定范围内的所有数字,并以降序对数字进行排序。
- 遍历上面存储的所有数字并生成严格小于给定数字范围的所有数字。
- 要生成小于给定数字的数字,请使用紧密变量,例如:
- Tight的值为0,表示通过包含该数字将使数字小于给定范围。
- Tight的值为1,表示通过包含该数字,它将给出大于给定范围的数字。因此,我们可以在获得紧的值1之后删除所有排列,以避免递归调用的数量增加。
- 生成所有数字排列之后,找到除以该数字的位数大于或等于K的数字。
- 将每个排列数字的计数存储在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());
// Intialise 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);
// Intialise 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
如果您希望与行业专家一起参加现场课程,请参阅《 Geeks现场课程》和《 Geeks现场课程美国》。