Count N digits numbers with sum divisible by K
Last Updated :
15 Nov, 2023
Given two integers N and K, the task is to count all N digits numbers whose sum of digits is divisible by K.
Examples:
Input: N = 2, K = 7
Output: 12
Explanation: 2 digits numbers with sum divisible by 7 are: {16, 25, 34, 43, 52, 59, 61, 68, 70, 77, 86, 95}.
Therefore, the required output is 12.
Input: N = 1, K = 2
Output: 4
Naive Approach: The simplest approach is to traverse all the numbers from the range [10(N - 1), 10N - 1] and check if the sum of all the digits of a number that lies within the range is divisible by K or not. For every number for which the condition is found to be true, increase count. Finally, print the count.
Time Complexity: O(10N - 10N - 1 - 1)
Auxiliary Space: O(1)
Efficient Approach: The idea is to use Digit DP technique to optimize the above approach. Below is the recurrence relation:
CountNum(N, sum, st) = \sum^{9}_{i=0} countNum(N - 1, (sum + i)\mod K, st)
sum: represents sum of digits
st: check if a number contains any leading 0.
Follow the steps below to solve the problem:
- Initialize a 3D array dp[N][K][st] to compute and store the values of all subproblems of the above recurrence relation.
- Finally, return the value of dp[N][sum%K][st].
C++
// C++ Program to implement
// the above approach
#include <bits/stdc++.h>
using namespace std;
#define M 1000
// Function to count the N digit numbers
// whose sum is divisible by K
int countNum(int N, int sum, int K,
bool st, int dp[M][M][2])
{
// Base case
if (N == 0 and sum == 0) {
return 1;
}
if (N < 0) {
return 0;
}
// If already computed
// subproblem occurred
if (dp[N][sum][st] != -1) {
return dp[N][sum][st];
}
// Store the count of N digit numbers
// whose sum is divisible by K
int res = 0;
// Check if the number does not contain
// any leading 0.
int start = st == 1 ? 0 : 1;
// Recurrence relation
for (int i = start; i <= 9; i++) {
res += countNum(N - 1, (sum + i) % K,
K, (st | i > 0), dp);
}
return dp[N][sum][st] = res;
}
// Driver Code
int main()
{
int N = 2, K = 7;
// Stores the values of
// overlapping subproblems
int dp[M][M][2];
memset(dp, -1, sizeof(dp));
cout << countNum(N, 0, K, 0, dp);
}
Java
// Java program to implement
// the above approach
import java.util.*;
import java.lang.*;
class GFG {
static final int M = 1000;
// Function to count the N digit numbers
// whose sum is divisible by K
static int countNum(int N, int sum, int K,
int st, int dp[][][])
{
// Base case
if (N == 0 && sum == 0) {
return 1;
}
if (N < 0) {
return 0;
}
// If already computed
// subproblem occurred
if (dp[N][sum][st] != -1) {
return dp[N][sum][st];
}
// Store the count of N digit numbers
// whose sum is divisible by K
int res = 0;
// Check if the number does not contain
// any leading 0.
int start = st == 1 ? 0 : 1;
// Recurrence relation
for (int i = start; i <= 9; i++) {
res += countNum(N - 1, (sum + i) % K,
K, ((st | i) > 0) ? 1 : 0, dp);
}
return dp[N][sum][st] = res;
}
// Driver code
public static void main(String[] args)
{
int N = 2, K = 7;
// Stores the values of
// overlapping subproblems
int[][][] dp = new int[M][M][2];
for (int[][] i : dp)
for (int[] j : i)
Arrays.fill(j, -1);
System.out.print(countNum(N, 0, K, 0, dp));
}
}
// This code is contributed by offbeat
Python3
# Python3 program to implement
# the above approach
# Function to count the N digit
# numbers whose sum is divisible by K
def countNum(N, sum, K, st, dp):
# Base case
if (N == 0 and sum == 0):
return 1
if (N < 0):
return 0
# If already computed
# subproblem occurred
if (dp[N][sum][st] != -1):
return dp[N][sum][st]
# Store the count of N digit
# numbers whose sum is divisible by K
res = 0
start = 1
# Check if the number does not contain
# any leading 0.
if (st == 1):
start = 0
else:
start = 1
# Recurrence relation
for i in range(start, 10):
min = 0
if ((st | i) > 0):
min = 1
else:
min = 0
res += countNum(N - 1, (sum + i) % K,
K, min, dp)
dp[N][sum][st] = res
return dp[N][sum][st]
# Driver code
if __name__ == '__main__':
N = 2
K = 7
M = 100
# Stores the values of
# overlapping subproblems
dp = [[[-1 for i in range(2)]
for j in range(M)]
for j in range(M)]
print(countNum(N, 0, K, 0, dp))
# This code is contributed by shikhasingrajput
C#
// C# program to implement
// the above approach
using System;
class GFG{
static int M = 1000;
// Function to count the N digit numbers
// whose sum is divisible by K
static int countNum(int N, int sum, int K,
int st, int[,, ] dp)
{
// Base case
if (N == 0 && sum == 0)
{
return 1;
}
if (N < 0)
{
return 0;
}
// If already computed
// subproblem occurred
if (dp[N, sum, st] != -1)
{
return dp[N, sum, st];
}
// Store the count of N digit numbers
// whose sum is divisible by K
int res = 0;
// Check if the number does not contain
// any leading 0.
int start = (st == 1 ? 0 : 1);
// Recurrence relation
for(int i = start; i <= 9; i++)
{
res += countNum(N - 1, (sum + i) % K,
K, ((st | i) > 0) ? 1 : 0, dp);
}
return dp[N, sum, st] = res;
}
// Driver code
static public void Main()
{
int N = 2, K = 7;
// Stores the values of
// overlapping subproblems
int[,, ] dp = new int[M, M, 2];
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;
Console.WriteLine(countNum(N, 0, K, 0, dp));
}
}
// This code is contributed by offbeat
JavaScript
<script>
// JavaScript Program to implement
// the above approach
var M = 1000;
// Function to count the N digit numbers
// whose sum is divisible by K
function countNum(N, sum, K, st, dp)
{
// Base case
if (N == 0 && sum == 0) {
return 1;
}
if (N < 0) {
return 0;
}
// If already computed
// subproblem occurred
if (dp[N][sum][st] != -1) {
return dp[N][sum][st];
}
// Store the count of N digit numbers
// whose sum is divisible by K
var res = 0;
// Check if the number does not contain
// any leading 0.
var start = st == 1 ? 0 : 1;
// Recurrence relation
for (var i = start; i <= 9; i++) {
res += countNum(N - 1, (sum + i) % K,
K, (st | i > 0), dp);
}
return dp[N][sum][st] = res;
}
// Driver Code
var N = 2, K = 7;
// Stores the values of
// overlapping subproblems
var dp = Array.from(Array(M), ()=>Array(M));
for(var i =0; i<M; i++)
for(var j =0; j<M; j++)
dp[i][j] = new Array(2).fill(-1);
document.write( countNum(N, 0, K, 0, dp));
</script>
Time Complexity:O(10*N*K)
Auxiliary Space: O(N*K)
Efficient approach : Using DP Tabulation method ( Iterative approach )
The approach to solve this problem is same but DP tabulation(bottom-up) method is better then Dp + memoization(top-down) because memoization method needs extra stack space of recursion calls.
Steps to solve this problem :
- Create a table 3D DP table to store the solution of the subproblems which is of length N * K * 2 (true , false) .
- Initialize the table with base cases
- Now Iterate over subproblems to get the value of current problem form previous computation of subproblems stored in DP
- Return the final solution stored in dp[N][sum][1] + dp[N][sum][0] .
Implementation :
C++
// C++ program for above approach
#include <bits/stdc++.h>
using namespace std;
// Function to count the N digit numbers
// whose sum is divisible by K
int countNum(int N, int sum, int K) {
// initialize DP to Store computations of subproblems
int dp[N + 1][K][2];
memset(dp, 0, sizeof(dp)); // fill DP with 0
// Base case
dp[0][0][0] = 1;
// Iterate over subproblems and get the solution
// of current problem with the help of recursion
for (int i = 1; i <= N; i++) {
for (int j = 0; j < K; j++) {
for (int k = (i == 1); k <= 9; k++) { // skip leading zeroes
int rem = (j - k % K + K) % K;
// get current value from previous computation
dp[i][j][1] += dp[i - 1][rem][1];
if (k > 0)
dp[i][j][1] += dp[i - 1][rem][0];
else
dp[i][j][0] += dp[i - 1][rem][0];
}
}
}
// return answer
return dp[N][sum][1] + dp[N][sum][0];
}
// Drivre Code
int main() {
int N = 2, K = 7;
// function call
cout << countNum(N, 0, K) << endl;
return 0;
}
// this code is contributed by bhardwajji
Java
import java.util.*;
public class Main {
// Function to count the N digit numbers
// whose sum is divisible by K
public static int countNum(int N, int sum, int K) {
// initialize DP to Store computations of subproblems
int[][][] dp = new int[N + 1][K][2];
for (int[][] row : dp)
for (int[] innerRow : row)
Arrays.fill(innerRow, 0); // fill DP with 0
// Base case
dp[0][0][0] = 1;
// Iterate over subproblems and get the solution
// of current problem with the help of recursion
for (int i = 1; i <= N; i++) {
for (int j = 0; j < K; j++) {
for (int k = (i == 1 ? 1 : 0); k <= 9; k++) { // skip leading zeroes
int rem = (j - k % K + K) % K;
// get current value from previous computation
dp[i][j][1] += dp[i - 1][rem][1];
if (k > 0)
dp[i][j][1] += dp[i - 1][rem][0];
else
dp[i][j][0] += dp[i - 1][rem][0];
}
}
}
// return answer
return dp[N][sum][1] + dp[N][sum][0];
}
// Driver Code
public static void main(String[] args) {
int N = 2, K = 7;
// function call
System.out.println(countNum(N, 0, K));
}
}
Python3
def count_num(n, sum_, k):
# initialize DP to Store computations of subproblems
dp = [[[0] * 2 for _ in range(k)] for _ in range(n + 1)]
# Base case
dp[0][0][0] = 1
# Iterate over subproblems and get the solution
# of current problem with the help of recursion
for i in range(1, n + 1):
for j in range(k):
for l in range(10):
if i == 1 and l == 0:
continue
rem = (j - l % k + k) % k
# get current value from previous computation
dp[i][j][1] += dp[i - 1][rem][1]
if l > 0:
dp[i][j][1] += dp[i - 1][rem][0]
else:
dp[i][j][0] += dp[i - 1][rem][0]
# return answer
return dp[n][sum_][1] + dp[n][sum_][0]
# Drivre Code
if __name__ == '__main__':
n = 2
k = 7
# function call
print(count_num(n, 0, k))
C#
using System;
class GFG {
// Function to count the N digit numbers
// whose sum is divisible by K
static int countNum(int N, int sum, int K)
{
// initialize DP to Store computations of
// subproblems
int[, , ] dp = new int[N + 1, K, 2];
Array.Clear(dp, 0, dp.Length); // fill DP with 0
// Base case
dp[0, 0, 0] = 1;
// Iterate over subproblems and get the solution
// of current problem with the help of recursion
for (int i = 1; i <= N; i++) {
for (int j = 0; j < K; j++) {
for (int k = (i == 1 ? 1 : 0); k <= 9;
k++) // skip leading zeroes
{
int rem = (j - k % K + K) % K;
// get current value from previous
// computation
dp[i, j, 1] += dp[i - 1, rem, 1];
if (k > 0)
dp[i, j, 1] += dp[i - 1, rem, 0];
else
dp[i, j, 0] += dp[i - 1, rem, 0];
}
}
}
// return answer
return dp[N, sum, 1] + dp[N, sum, 0];
}
// Drivre Code
static void Main()
{
int N = 2;
int K = 7;
Console.WriteLine(countNum(N, 0, K));
}
}
JavaScript
function countNum(N, sum, K) {
// initialize DP to Store computations of subproblems
let dp = new Array(N + 1);
for (let i = 0; i <= N; i++) {
dp[i] = new Array(K);
for (let j = 0; j < K; j++) {
dp[i][j] = new Array(2).fill(0);
}
}
// Base case
dp[0][0][0] = 1;
// Iterate over subproblems and get the solution
// of current problem with the help of recursion
for (let i = 1; i <= N; i++) {
for (let j = 0; j < K; j++) {
for (let k = (i == 1 ? 1 : 0); k <= 9; k++) { // skip leading zeroes
let rem = (j - k % K + K) % K;
// get current value from previous computation
dp[i][j][1] += dp[i - 1][rem][1];
if (k > 0)
dp[i][j][1] += dp[i - 1][rem][0];
else
dp[i][j][0] += dp[i - 1][rem][0];
}
}
}
// return answer
return dp[N][sum][1] + dp[N][sum][0];
}
// Driver Code
let N = 2, K = 7;
console.log(countNum(N, 0, K));
Time Complexity: O(10*N*K)
Auxiliary Space: O(N*K)
Optmized approach: Using DP Tabulation method (Iterative approach) with Space Optimization
The approach to solve this problem remains the same, but we will utilize DP tabulation (bottom-up) method with a space-efficient optimization using "prev" and "curr" arrays.
Steps to solve this problem using space optimization:
- Create two 2D arrays: "prev" and "curr", each of length [K][2].
- Initialize the "prev" array with base cases.
- Iterate over the subproblems to compute the current problem's value using the previous computations stored in the "prev" array.
- Update the "curr" array with the calculated values for the current iteration.
- Copy the values of the "curr" array to the "prev" array, and reset the "curr" array to all zeros for the next iteration.
- Finally, return the final solution stored in prev[sum][1] + prev[sum][0].
Below is the implementation of the above approach:
C++
// C++ implementation of space optmized approach
#include <bits/stdc++.h>
using namespace std;
int countNum(int N, int sum, int K)
{
int prev[K][2] = { 0 };
int curr[K][2] = { 0 };
// Base case
prev[0][0] = 1;
for (int i = 1; i <= N; i++) {
for (int j = 0; j < K; j++) {
for (int k = (i == 1); k <= 9;
k++) { // skip leading zeroes
int rem = (j - k % K + K) % K;
curr[j][1] += prev[rem][1];
if (k > 0)
curr[j][1] += prev[rem][0];
else
curr[j][0] += prev[rem][0];
}
}
// Copy curr to prev for the next iteration
for (int j = 0; j < K; j++) {
prev[j][0] = curr[j][0];
prev[j][1] = curr[j][1];
curr[j][0] = 0;
curr[j][1] = 0;
}
}
return prev[sum][1] + prev[sum][0];
}
int main()
{
int N = 2, K = 7;
cout << countNum(N, 0, K) << endl;
return 0;
}
// This code is contributed by Tapesh(tapeshdua420)
Java
// Java implementation of space optimized approach
import java.util.*;
class Main {
public static int countNum(int N, int sum, int K) {
int[][] prev = new int[K][2];
int[][] curr = new int[K][2];
// Base case
prev[0][0] = 1;
for (int i = 1; i <= N; i++) {
for (int j = 0; j < K; j++) {
for (int k = (i == 1) ? 1 : 0; k <= 9; k++) { // skip leading zeroes
int rem = (j - k % K + K) % K;
curr[j][1] += prev[rem][1];
if (k > 0)
curr[j][1] += prev[rem][0];
else
curr[j][0] += prev[rem][0];
}
}
// Copy curr to prev for the next iteration
for (int j = 0; j < K; j++) {
prev[j][0] = curr[j][0];
prev[j][1] = curr[j][1];
curr[j][0] = 0;
curr[j][1] = 0;
}
}
return prev[sum][1] + prev[sum][0];
}
public static void main(String[] args) {
int N = 2, K = 7;
System.out.println(countNum(N, 0, K));
}
}
// This code is contributed by Tapesh(tapeshdua420)
Python3
# Python implementation:
def countNum(N, sum, K):
prev = [[0, 0] for _ in range(K)]
curr = [[0, 0] for _ in range(K)]
# Base case
prev[0][0] = 1
for i in range(1, N+1):
for j in range(K):
for k in range(int(i == 1), 10): # skip leading zeroes
rem = (j - k % K + K) % K
curr[j][1] += prev[rem][1]
if k > 0:
curr[j][1] += prev[rem][0]
else:
curr[j][0] += prev[rem][0]
# Copy curr to prev for the next iteration
for j in range(K):
prev[j][0] = curr[j][0]
prev[j][1] = curr[j][1]
curr[j][0] = 0
curr[j][1] = 0
return prev[sum][1] + prev[sum][0]
N = 2
K = 7
print(countNum(N, 0, K))
# This code is contributed by Tapesh(tapeshdu420)
C#
// C# implementation
using System;
class MainClass {
public static int CountNum(int N, int sum, int K) {
int[,] prev = new int[K, 2];
int[,] curr = new int[K, 2];
// Base case
prev[0, 0] = 1;
for (int i = 1; i <= N; i++) {
for (int j = 0; j < K; j++) {
for (int k = (i == 1) ? 1 : 0; k <= 9; k++) { // skip leading zeroes
int rem = (j - k % K + K) % K;
curr[j, 1] += prev[rem, 1];
if (k > 0)
curr[j, 1] += prev[rem, 0];
else
curr[j, 0] += prev[rem, 0];
}
}
// Copy curr to prev for the next iteration
for (int j = 0; j < K; j++) {
prev[j, 0] = curr[j, 0];
prev[j, 1] = curr[j, 1];
curr[j, 0] = 0;
curr[j, 1] = 0;
}
}
return prev[sum, 1] + prev[sum, 0];
}
public static void Main(string[] args) {
int N = 2, K = 7;
Console.WriteLine(CountNum(N, 0, K));
}
}
// This code is contributed by Sakshi
JavaScript
function countNum(N, sum, K) {
const prev = new Array(K).fill(0).map(() => new Array(2).fill(0));
const curr = new Array(K).fill(0).map(() => new Array(2).fill(0));
// Base case
prev[0][0] = 1;
for (let i = 1; i <= N; i++) {
for (let j = 0; j < K; j++) {
for (let k = (i === 1) ? 1 : 0; k <= 9; k++) { // skip leading zeroes
const rem = (j - (k % K) + K) % K;
curr[j][1] += prev[rem][1];
if (k > 0) {
curr[j][1] += prev[rem][0];
} else {
curr[j][0] += prev[rem][0];
}
}
}
// Copy curr to prev for the next iteration
for (let j = 0; j < K; j++) {
prev[j][0] = curr[j][0];
prev[j][1] = curr[j][1];
curr[j][0] = 0;
curr[j][1] = 0;
}
}
return prev[sum][1] + prev[sum][0];
}
const N = 2;
const K = 7;
console.log(countNum(N, 0, K));
// This code is contributed by shivamgupta0987654321
Time Complexity: O(10*N*K)
Auxiliary Space: O(K)
Similar Reads
DSA Tutorial - Learn Data Structures and Algorithms
DSA (Data Structures and Algorithms) is the study of organizing data efficiently using data structures like arrays, stacks, and trees, paired with step-by-step procedures (or algorithms) to solve problems effectively. Data structures manage how data is stored and accessed, while algorithms focus on
7 min read
Quick Sort
QuickSort is a sorting algorithm based on the Divide and Conquer that picks an element as a pivot and partitions the given array around the picked pivot by placing the pivot in its correct position in the sorted array. It works on the principle of divide and conquer, breaking down the problem into s
12 min read
Merge Sort - Data Structure and Algorithms Tutorials
Merge sort is a popular sorting algorithm known for its efficiency and stability. It follows the divide-and-conquer approach. It works by recursively dividing the input array into two halves, recursively sorting the two halves and finally merging them back together to obtain the sorted array. Merge
14 min read
Breadth First Search or BFS for a Graph
Given a undirected graph represented by an adjacency list adj, where each adj[i] represents the list of vertices connected to vertex i. Perform a Breadth First Search (BFS) traversal starting from vertex 0, visiting vertices from left to right according to the adjacency list, and return a list conta
15+ min read
Bubble Sort Algorithm
Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if they are in the wrong order. This algorithm is not suitable for large data sets as its average and worst-case time complexity are quite high.We sort the array using multiple passes. After the fir
8 min read
Binary Search Algorithm - Iterative and Recursive Implementation
Binary Search Algorithm is a searching algorithm used in a sorted array by repeatedly dividing the search interval in half. The idea of binary search is to use the information that the array is sorted and reduce the time complexity to O(log N). Binary Search AlgorithmConditions to apply Binary Searc
15 min read
Insertion Sort Algorithm
Insertion sort is a simple sorting algorithm that works by iteratively inserting each element of an unsorted list into its correct position in a sorted portion of the list. It is like sorting playing cards in your hands. You split the cards into two groups: the sorted cards and the unsorted cards. T
9 min read
Data Structures Tutorial
Data structures are the fundamental building blocks of computer programming. They define how data is organized, stored, and manipulated within a program. Understanding data structures is very important for developing efficient and effective algorithms. What is Data Structure?A data structure is a st
2 min read
Dijkstra's Algorithm to find Shortest Paths from a Source to all
Given a weighted undirected graph represented as an edge list and a source vertex src, find the shortest path distances from the source vertex to all other vertices in the graph. The graph contains V vertices, numbered from 0 to V - 1.Note: The given graph does not contain any negative edge. Example
12 min read
Selection Sort
Selection Sort is a comparison-based sorting algorithm. It sorts an array by repeatedly selecting the smallest (or largest) element from the unsorted portion and swapping it with the first unsorted element. This process continues until the entire array is sorted.First we find the smallest element an
8 min read