给定两个整数N和K ,任务是计算数字和为K的倍数的[0,N]范围内的整数数。答案可能很大,因此以10 9 +7为模数打印答案。
例子:
Input: N = 10, K = 5
Output: 2
0 and 5 are the only possible integers.
Input: N = 30, K = 4
Output: 7
天真的方法:对于较小的N ,请在[0,N]范围内循环,并检查数字的总和是否为K的倍数。
高效的方法:想法是使用digit dp解决此问题。将解决给定整数中从左或最高有效位(MSD)遍历所有索引值的子问题,并且对于每个索引,存储使( K到当前索引的位数的总和)mod K为零的方式数。 dp状态将为:
dp[idx][sum][tight]
idx = position, it tells about the index value from left in the given integer
sum = sum of digits mod k, This parameter will store the (sum of digits mod k) in the generated integer from most significant digit(MSD) to p
tight = flag if the current value is crossing the range (1, n) or not
For unrestricted range tight = 0
For restricted range tight = 1
假设我们在MSD上具有索引idx 。因此,最初的总和为0 。在每个位置处设置一个始终在[0,9]范围内的极限。
因此,用从0到极限的数字填充索引处的数字,并从索引= idx + 1的下一个状态获取答案,并分别计算下一个状态的new_tight 。 dp状态定义为:
dp[idx][sum][tight] += dp[idx + 1][(sum + d) % k][new_tight]
for d in [0, limit]
下面是上述方法的实现:
C++
// C++ implementation of the approach
#include
using namespace std;
#define MAX 100005
#define MOD 1000000007
// To store the states of the dp
int dp[MAX][101][2];
// Function to return the count of numbers
// from the range [0, n] whose digit sum
// is a multiple of k using bottom-up dp
int countNum(int idx, int sum, int tight,
vector num, int len, int k)
{
if (len == idx) {
if (sum == 0)
return 1;
else
return 0;
}
if (dp[idx][sum][tight] != -1)
return dp[idx][sum][tight];
int res = 0, limit;
// The digit in this index can
// only be from [0, num[idx]]
if (tight == 0) {
limit = num[idx];
}
// The digit in this index can
// be anything from [0, 9]
else {
limit = 9;
}
for (int i = 0; i <= limit; i++) {
// new_tight is the flag value
// for the next position
int new_tight = tight;
if (tight == 0 && i < limit)
new_tight = 1;
res += countNum(idx + 1,
(sum + i) % k, new_tight,
num, len, k);
res %= MOD;
}
// res can't be negative
if (res < 0)
res += MOD;
return dp[idx][sum][tight] = res;
}
// Function to process the string to
// a vector of digits from MSD to LSD
vector process(string s)
{
vector num;
for (int i = 0; i < s.length(); i++) {
num.push_back(s[i] - '0');
}
return num;
}
// Driver code
int main()
{
// For large input number n
string n = "98765432109876543210";
// Total number of digits in n
int len = n.length();
int k = 58;
// Clean dp table
memset(dp, -1, sizeof(dp));
// Process the string to a vector
// of digits from MSD to LSD
vector num = process(n);
cout << countNum(0, 0, 0, num, len, k);
return 0;
}
Java
// Java implementation of the approach
import java.util.*;
class GFG
{
static final int MAX = 100005;
static final int MOD = 1000000007;
// To store the states of the dp
static int [][][]dp = new int[MAX][101][2];
// Function to return the count of numbers
// from the range [0, n] whose digit sum
// is a multiple of k using bottom-up dp
static int countNum(int idx, int sum, int tight,
Vector num, int len, int k)
{
if (len == idx)
{
if (sum == 0)
return 1;
else
return 0;
}
if (dp[idx][sum][tight] != -1)
return dp[idx][sum][tight];
int res = 0, limit;
// The digit in this index can
// only be from [0, num[idx]]
if (tight == 0)
{
limit = num.get(idx);
}
// The digit in this index can
// be anything from [0, 9]
else
{
limit = 9;
}
for (int i = 0; i <= limit; i++)
{
// new_tight is the flag value
// for the next position
int new_tight = tight;
if (tight == 0 && i < limit)
new_tight = 1;
res += countNum(idx + 1,
(sum + i) % k, new_tight,
num, len, k);
res %= MOD;
}
// res can't be negative
if (res < 0)
res += MOD;
return dp[idx][sum][tight] = res;
}
// Function to process the String to
// a vector of digits from MSD to LSD
static Vector process(String s)
{
Vector num = new Vector();
for (int i = 0; i < s.length(); i++)
{
num.add(s.charAt(i) - '0');
}
return num;
}
// Driver code
public static void main(String[] args)
{
// For large input number n
String n = "98765432109876543210";
// Total number of digits in n
int len = n.length();
int k = 58;
// Clean dp table
for(int i = 0; i < MAX; i++)
{
for(int j = 0; j < 101; j++)
{
for(int l = 0; l < 2; l++)
dp[i][j][l] = -1;
}
}
// Process the String to a vector
// of digits from MSD to LSD
Vector num = process(n);
System.out.print(countNum(0, 0, 0, num, len, k));
}
}
// This code is contributed by 29AjayKumar
Python 3
# Python 3 implementation of the approach
MAX = 10005
MOD = 1000000007
# Function to return the count of numbers
# from the range [0, n] whose digit sum
# is a multiple of k using bottom-up dp
def countNum(idx, sum, tight, num, len1, k):
if (len1 == idx):
if (sum == 0):
return 1
else:
return 0
if (dp[idx][sum][tight] != -1):
return dp[idx][sum][tight]
res = 0
# The digit in this index can
# only be from [0, num[idx]]
if (tight == 0):
limit = num[idx]
# The digit in this index can
# be anything from [0, 9]
else:
limit = 9
for i in range(limit + 1):
# new_tight is the flag value
# for the next position
new_tight = tight
if (tight == 0 and i < limit):
new_tight = 1
res += countNum(idx + 1,(sum + i) % k,
new_tight, num, len1, k)
res %= MOD
# res can't be negative
if (res < 0):
res += MOD
dp[idx][sum][tight] = res
return dp[idx][sum][tight]
# Function to process the string to
# a vector of digits from MSD to LSD
def process(s):
num = []
for i in range(len(s)):
num.append(ord(s[i]) - ord('0'))
return num
# Driver code
if __name__ == '__main__':
# For large input number n
n = "98765432109876543210"
# Total number of digits in n
len1 = len(n)
k = 58
# To store the states of the dp
dp = [[[-1 for i in range(2)]
for j in range(101)]
for k in range(MAX)]
# Process the string to a vector
# of digits from MSD to LSD
num = process(n)
print(countNum(0, 0, 0, num, len1, k))
# This code is contributed by Surendra_Gangwar
C#
// C# implementation of the approach
using System;
using System.Collections.Generic;
class GFG
{
static readonly int MAX = 10005;
static readonly int MOD = 1000000007;
// To store the states of the dp
static int [,,]dp = new int[MAX, 101, 2];
// Function to return the count of numbers
// from the range [0, n] whose digit sum
// is a multiple of k using bottom-up dp
static int countNum(int idx, int sum, int tight,
List num, int len, int k)
{
if (len == idx)
{
if (sum == 0)
return 1;
else
return 0;
}
if (dp[idx, sum, tight] != -1)
return dp[idx, sum, tight];
int res = 0, limit;
// The digit in this index can
// only be from [0, num[idx]]
if (tight == 0)
{
limit = num[idx];
}
// The digit in this index can
// be anything from [0, 9]
else
{
limit = 9;
}
for (int i = 0; i <= limit; i++)
{
// new_tight is the flag value
// for the next position
int new_tight = tight;
if (tight == 0 && i < limit)
new_tight = 1;
res += countNum(idx + 1,
(sum + i) % k, new_tight,
num, len, k);
res %= MOD;
}
// res can't be negative
if (res < 0)
res += MOD;
return dp[idx, sum, tight] = res;
}
// Function to process the String to
// a vector of digits from MSD to LSD
static List process(String s)
{
List num = new List();
for (int i = 0; i < s.Length; i++)
{
num.Add(s[i] - '0');
}
return num;
}
// Driver code
public static void Main(String[] args)
{
// For large input number n
String n = "98765432109876543210";
// Total number of digits in n
int len = n.Length;
int k = 58;
// Clean dp table
for(int i = 0; i < MAX; i++)
{
for(int j = 0; j < 101; j++)
{
for(int l = 0; l < 2; l++)
dp[i, j, l] = -1;
}
}
// Process the String to a vector
// of digits from MSD to LSD
List num = process(n);
Console.Write(countNum(0, 0, 0, num, len, k));
}
}
// This code is contributed by Rajput-Ji
635270835