Open In App

Count numbers in a range with digit sum divisible by K having first and last digit different

Improve
Improve
Like Article
Like
Save
Share
Report

Given a range in the form of L and R, and a value K, the task is to count the numbers between the range L to R such that the sum of the digit is divisible by K, and the first digit is not equal to the last digit of the number. 

Examples: 

Input: L = 1, R = 10, K = 2 
Output:
Explanation: 
The input numbers whose sum of the digits are divisible by 2 (K = 2) are 2, 4, 6, 8 but the first digit of all these numbers is equal to the last digit. Therefore the answer is 0, which means none of the digit satisfies the condition.

Input: L = 10, R = 20, K = 2 
Output:
Explanation: 
The numbers whose sum of digits are divisible by 2 (K = 2) are 11, 13, 15, 17, 19, and 20. But in 11 the first digit matches with the last digit. So excluding this, the answer will be 5. 

Naive Approach:
The naive approach for solving this problem is to check every number whether it satisfies the given condition or not. However, this solution will work inefficiently because of the range of numbers. 

Efficient approach:
The main idea for solving this problem is to use Digit Dynamic Programming
There are various states that need to be defined for the transition. So, we can define the DP states as:  

  1. Since in digit DP, we think a number as a sequence of digits so for traversing the sequence, we need position as a state. What we generally do, is place every possible digit [0, 9] in the recursive call, which we can put at every position to find possible solution provided all conditions should be satisfied.
  2. The second thing which we need is the sum, upto which we have built our sequence. Since the sum can be large, so we can keep sum as sum modulo K for better space and time complexity. So the sum is the second DP state.
  3. The third thing which we need is the digit, which we have placed in the first position. While placing the last digit of the sequence, we need to check whether it is equal to the first digit, which we have placed earlier or not. So, this is going to be the third DP state.
  4. The problem is at each time we create an N-digit sequence, where N is the number of digits present in the upper bound of the number, it doesn’t create 1 if (N=3), but it creates 001. Here the first digit is 0, and the last digit is 1, but this is wrong as we have created a single-digit number whose first digit is equal to the last digit. So, we have to check this every single time in the recursive call. Let suppose we have to build a sequence like 0002 where we need to update our starting digit equal to 2. So a prefix of zeros should be neglected. To check this, we need a new boolean state (0 or 1). This is the fourth DP state.
  5. The last thing which we need is to check whether the number which we are building does not exceed the upper bound. Let suppose we are building a number less than equal to 456. We have created a sequence like 45, so at the 3rd place, we can’t put any digit between 0 to 9. Here we can place only numbers from 0 to 6. So for checking this bound, we need an extra boolean state. This is our last DP state.

Algorithm: 

  • At every position, we calculate the sum of digits and remove the prefix of zeros.
  • The first non-zero digit will be our starting digit of the sequence.
  • In the last position, we will check whether it is matching with the starting digit or not.

Below is the implementation of the above approach. 

C++




// C++ Program to count numbers
// in a range with digit sum
// divisible by K having first
// and last digit different
 
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
 
ll K;
ll N;
vector<int> v;
ll dp[20][1000][10][2][2];
 
void init(ll x)
{
    memset(dp, -1, sizeof(dp));
 
    // For calculating the upper
    // bound of the sequence.
    v.clear();
    while (x > 0) {
        v.push_back(x % 10);
        x /= 10;
    }
 
    reverse(v.begin(), v.end());
 
    N = v.size();
}
 
ll fun(ll pos, ll sum, ll st, ll check, ll f)
{
    if (pos == N) {
 
        // checking whether the sum
        // of digits = 0 or not
        // and the corner case
        // as number equal to 1
        return (sum == 0
                and check == 1);
    }
 
    // If the state is visited
    // then return the answer
    // of this state directly.
    if (dp[pos][sum][st][check][f] != -1)
        return dp[pos][sum][st][check][f];
 
    ll lmt = 9;
 
    // for checking whether digit
    // to be placed is up to
    // upper bound at the
    // position pos or upto 9
    if (!f)
        lmt = v[pos];
 
    ll ans = 0;
    for (int digit = 0; digit <= lmt; digit++) {
 
        ll nf = f;
 
        // calculating new digit
        // sum modulo k
        ll new_sum = (sum + digit) % K;
 
        ll new_check = check;
        ll new_st = st;
        if (f == 0 and digit < lmt)
            nf = 1;
 
        // check if there is a prefix
        // of 0s and current digit != 0
        if (check == 0 and digit != 0) {
 
            // Then current digit will
            // be the starting digit
            new_st = digit;
 
            // Update the boolean flag
            // that the starting digit
            // has been found
            new_check = 1;
        }
 
        // At n-1, check if current digit
        // and starting digit are the same
        // then no need to calculate this answer.
        if (pos == N - 1
            and new_st == digit)
            continue;
 
        // Else find the answer
        ans += fun(pos + 1,
                   new_sum,
                   new_st,
                   new_check, nf);
    }
 
    return dp[pos][sum][st][check][f] = ans;
}
 
// Function to find the required count
void findCount(int L, int R, int K)
{
 
    // Setting up the upper bound
    init(R);
 
    // calculating F(R)
    ll r_ans = fun(0, 0, 0, 0, 0);
 
    // Setting up the upper bound
    init(L - 1);
 
    // calculating F(L-1)
    ll l_ans = fun(0, 0, 0, 0, 0);
 
    cout << r_ans - l_ans;
}
 
// Driver code
int main()
{
    ll L = 10;
    ll R = 20;
    K = 2;
 
    findCount(L, R, K);
 
    return 0;
}


Java




// Java Program to count numbers
// in a range with digit sum
// divisible by K having first
// and last digit different
import java.util.*;
 
class GFG{
 
static int K;
static int N;
static Vector<Integer> v = new Vector<>();
static int [][][][][]dp = new int[20][1000][10][2][2];
 
static void init(int x)
{
    for(int i = 0; i < 20; i++)
        for(int j = 0; j < 1000; j++)
            for(int k = 0; k < 10; k++)
                for(int l = 0; l < 2; l++)
                    for(int m = 0; m < 2; m++)
                        dp[i][j][k][l][m] = -1;
 
    // For calculating the upper
    // bound of the sequence.
    v.clear();
    while (x > 0)
    {
        v.add(x % 10);
        x /= 10;
    }
    Collections.reverse(v);
    N = v.size();
}
 
static int fun(int pos, int sum,
               int st, int check, int f)
{
    if (pos == N)
    {
 
        // checking whether the sum
        // of digits = 0 or not
        // and the corner case
        // as number equal to 1
        return (sum == 0 && check == 1) ? 1 : 0;
    }
 
    // If the state is visited
    // then return the answer
    // of this state directly.
    if (dp[pos][sum][st][check][f] != -1)
        return dp[pos][sum][st][check][f];
 
    int lmt = 9;
 
    // for checking whether digit
    // to be placed is up to
    // upper bound at the
    // position pos or upto 9
    if (f == 0)
        lmt = v.get(pos);
 
    int ans = 0;
    for (int digit = 0; digit <= lmt; digit++)
    {
        int nf = f;
 
        // calculating new digit
        // sum modulo k
        int new_sum = (sum + digit) % K;
 
        int new_check = check;
        int new_st = st;
        if (f == 0 && digit < lmt)
            nf = 1;
 
        // check if there is a prefix
        // of 0s and current digit != 0
        if (check == 0 && digit != 0)
        {
 
            // Then current digit will
            // be the starting digit
            new_st = digit;
 
            // Update the boolean flag
            // that the starting digit
            // has been found
            new_check = 1;
        }
 
        // At n-1, check if current digit
        // and starting digit are the same
        // then no need to calculate this answer.
        if (pos == N - 1 && new_st == digit)
            continue;
 
        // Else find the answer
        ans += fun(pos + 1, new_sum,
                    new_st, new_check, nf);
    }
 
    return dp[pos][sum][st][check][f] = ans;
}
 
// Function to find the required count
static void findCount(int L, int R, int K)
{
 
    // Setting up the upper bound
    init(R);
 
    // calculating F(R)
    int r_ans = fun(0, 0, 0, 0, 0);
 
    // Setting up the upper bound
    init(L - 1);
 
    // calculating F(L-1)
    int l_ans = fun(0, 0, 0, 0, 0);
 
    System.out.print(r_ans - l_ans);
}
 
// Driver code
public static void main(String[] args)
{
    int L = 10;
    int R = 20;
    K = 2;
 
    findCount(L, R, K);
}
}
 
// This code contributed by PrinciRaj1992


Python3




# Python3 program to count numbers
# in a range with digit sum
# divisible by K having first
# and last digit different
K = 0
N = 0
v = []
dp = [[[[[-1 for i in range(2)]
             for j in range(2)]
             for k in range(10)]
             for l in range(1000)]
             for m in range(20)]
 
def init(x):
     
    # For calculating the upper
    # bound of the sequence.
    global K
    global N
    global v
    v = []
     
    while (x > 0):
        v.append(x % 10)
        x //= 10
 
    v = v[::-1]
 
    N = len(v)
 
def fun(pos, sum, st, check, f):
     
    global N
    global v
     
    if (pos == N):
         
        # Checking whether the sum of
        # digits = 0 or not and the
        # corner case as number equal to 1
        return (sum == 0 and check == 1)
 
    # If the state is visited
    # then return the answer
    # of this state directly.
    if (dp[pos][sum][st][check][f] != -1):
        return dp[pos][sum][st][check][f]
 
    lmt = 9
 
    # For checking whether digit to
    # be placed is up to upper bound
    # at the position pos or upto 9
    if (f == False):
        lmt = v[pos]
 
    ans = 0
    for digit in range(lmt + 1):
        nf = f
 
        # Calculating new digit
        # sum modulo k
        new_sum = (sum + digit) % K
 
        new_check = check
        new_st = st
         
        if (f == 0 and digit < lmt):
            nf = 1
 
        # Check if there is a prefix
        # of 0s and current digit != 0
        if (check == 0 and digit != 0):
             
            # Then current digit will
            # be the starting digit
            new_st = digit
 
            # Update the boolean flag
            # that the starting digit
            # has been found
            new_check = 1
 
        # At n-1, check if current digit
        # and starting digit are the same
        # then no need to calculate this answer
        if (pos == N - 1 and new_st == digit):
            continue
 
        # Else find the answer
        ans += fun(pos + 1, new_sum,
                   new_st, new_check, nf)
     
        dp[pos][sum][st][check][f] = ans
 
    return ans
 
# Function to find the required count
def findCount(L, R, K):
     
    # Setting up the upper bound
    init(R)
 
    # calculating F(R)
    r_ans = fun(0, 0, 0, 0, 0)
 
    # Setting up the upper bound
    init(L - 1)
 
    # Calculating F(L-1)
    r_ans = 0
    l_ans = fun(0, 0, 0, 0, 0)
 
    print(l_ans - r_ans)
 
# Driver code
if __name__ == '__main__':
     
    L = 10
    R = 20
    K = 2
 
    findCount(L, R, K)
 
# This code is contributed by Surendra_Gangwar


C#




// C# program to count numbers
// in a range with digit sum
// divisible by K having first
// and last digit different
using System;
using System.Collections.Generic;
 
class GFG{
 
static int K;
static int N;
static List<int> v = new List<int>();
static int [,,,,]dp = new int[20, 1000, 10, 2, 2];
 
static void init(int x)
{
    for(int i = 0; i < 20; i++)
       for(int j = 0; j < 1000; j++)
          for(int k = 0; k < 10; k++)
             for(int l = 0; l < 2; l++)
                for(int m = 0; m < 2; m++)
                   dp[i, j, k, l, m] = -1;
 
    // For calculating the upper
    // bound of the sequence.
    v.Clear();
    while (x > 0)
    {
        v.Add(x % 10);
        x /= 10;
    }
    v.Reverse();
    N = v.Count;
}
 
static int fun(int pos, int sum, int st, 
               int check, int f)
{
    if (pos == N)
    {
 
        // Checking whether the sum
        // of digits = 0 or not
        // and the corner case
        // as number equal to 1
        return (sum == 0 && check == 1) ? 1 : 0;
    }
 
    // If the state is visited
    // then return the answer
    // of this state directly.
    if (dp[pos, sum, st, check, f] != -1)
        return dp[pos, sum, st, check, f];
 
    int lmt = 9;
 
    // For checking whether digit
    // to be placed is up to
    // upper bound at the
    // position pos or upto 9
    if (f == 0)
        lmt = v[pos];
 
    int ans = 0;
    for(int digit = 0; digit <= lmt; digit++)
    {
       int nf = f;
        
       // Calculating new digit
       // sum modulo k
       int new_sum = (sum + digit) % K;
       int new_check = check;
       int new_st = st;
        
       if (f == 0 && digit < lmt)
           nf = 1;
        
       // Check if there is a prefix
       // of 0s and current digit != 0
       if (check == 0 && digit != 0)
       {
            
           // Then current digit will
           // be the starting digit
           new_st = digit;
            
           // Update the bool flag
           // that the starting digit
           // has been found
           new_check = 1;
       }
        
       // At n-1, check if current digit
       // and starting digit are the same
       // then no need to calculate this answer.
       if (pos == N - 1 && new_st == digit)
           continue;
        
       // Else find the answer
       ans += fun(pos + 1, new_sum,
                  new_st, new_check, nf);
    }
    return dp[pos, sum, st, check, f] = ans;
}
 
// Function to find the required count
static void findCount(int L, int R, int K)
{
 
    // Setting up the upper bound
    init(R);
 
    // Calculating F(R)
    int r_ans = fun(0, 0, 0, 0, 0);
 
    // Setting up the upper bound
    init(L - 1);
 
    // calculating F(L-1)
    int l_ans = fun(0, 0, 0, 0, 0);
 
    Console.Write(r_ans - l_ans);
}
 
// Driver code
public static void Main(String[] args)
{
    int L = 10;
    int R = 20;
    K = 2;
 
    findCount(L, R, K);
}
}
 
// This code is contributed by 29AjayKumar


Javascript




<script>
  
// Javascript program to count numbers
// in a range with digit sum
// divisible by K having first
// and last digit different
var K;
var N;
var v = [];
var dp = Array.from(Array(20));
 
function init( x)
{
    for(var i = 0; i< 20;i++)
{
    dp[i] = Array.from(Array(1000),()=>Array(10));
 
    for(var j =0; j<1000;j++)
    {
        for(var k = 0; k<10;k++)
        {
            dp[i][j][k] = Array.from(Array(2), ()=>Array(2).fill(-1));
        }
    }
}
 
    // For calculating the upper
    // bound of the sequence.
    v = [];
    while (x > 0)
    {
        v.push(x % 10);
        x = parseInt(x/10);
    }
    v.reverse();
    N = v.length;
}
 
function fun(pos, sum, st,  check, f)
{
    if (pos == N)
    {
 
        // Checking whether the sum
        // of digits = 0 or not
        // and the corner case
        // as number equal to 1
        return (sum == 0 && check == 1) ? 1 : 0;
    }
 
    // If the state is visited
    // then return the answer
    // of this state directly.
    if (dp[pos][sum][st][check][f] != -1)
        return dp[pos][sum][st][check][f];
 
    var lmt = 9;
 
    // For checking whether digit
    // to be placed is up to
    // upper bound at the
    // position pos or upto 9
    if (f == 0)
        lmt = v[pos];
 
    var ans = 0;
    for(var digit = 0; digit <= lmt; digit++)
    {
       var nf = f;
        
       // Calculating new digit
       // sum modulo k
       var new_sum = (sum + digit) % K;
       var new_check = check;
       var new_st = st;
        
       if (f == 0 && digit < lmt)
           nf = 1;
        
       // Check if there is a prefix
       // of 0s and current digit != 0
       if (check == 0 && digit != 0)
       {
            
           // Then current digit will
           // be the starting digit
           new_st = digit;
            
           // Update the bool flag
           // that the starting digit
           // has been found
           new_check = 1;
       }
        
       // At n-1, check if current digit
       // and starting digit are the same
       // then no need to calculate this answer.
       if (pos == N - 1 && new_st == digit)
           continue;
        
       // Else find the answer
       ans += fun(pos + 1, new_sum,
                  new_st, new_check, nf);
    }
    return dp[pos][sum][st][check][f] = ans;
}
 
// Function to find the required count
function findCount(L, R, K)
{
 
    // Setting up the upper bound
    init(R);
 
    // Calculating F(R)
    var r_ans = fun(0, 0, 0, 0, 0);
 
    // Setting up the upper bound
    init(L - 1);
 
    // calculating F(L-1)
    var l_ans = fun(0, 0, 0, 0, 0);
 
    document.write(r_ans - l_ans);
}
 
// Driver code
var L = 10;
var R = 20;
K = 2;
findCount(L, R, K);
 
// This code is contributed by itsok.
</script>


Output: 

5

 

Time Complexity: O(18*1000*10*2*2), which is nearly O(2*10^6).

Auxiliary Space: O(800*1000)
 



Last Updated : 10 Nov, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads