Open In App

Number of subsets with sum divisible by M | Set 2

Given an array arr[] of N integers and an integer M, the task is to find the number of non-empty subsequences such that the sum of the subsequence is divisible by M.

Examples:  



Input: arr[] = {1, 2, 3}, M = 1 
Output:
Number of non-empty subsets of this array are 7. 
Since m = 1, m will divide all the possible subsets.
Input: arr[] = {1, 2, 3}, M = 2 
Output: 3  

Approach: A dynamic programming-based approach with O(N * SUM) time complexity where N is the length of the array and SUM is the sum of all the array elements has been discussed in this article.
In this article, an improvement over the previous approach will be discussed. Instead of the sum as one of the states of DP, (sum % m) will be used as one of the states of the DP as it is sufficient. Thus, the time complexity boils down to O(m * N).
Recurrence relation: 



dp[i][curr] = dp[i + 1][(curr + arr[i]) % m] + dp[i + 1][curr]  

Let’s define the states now, dp[i][curr] simply means number of subsets of the sub-array arr[i…N-1] such that (sum of its element + curr) % m = 0.

Below is the implementation of the above approach:  




// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
#define maxN 20
#define maxM 10
 
// To store the states of DP
int dp[maxN][maxM];
bool v[maxN][maxM];
 
// Function to find the required count
int findCnt(int* arr, int i, int curr, int n, int m)
{
    // Base case
    if (i == n) {
        if (curr == 0)
            return 1;
        else
            return 0;
    }
 
    // If the state has been solved before
    // return the value of the state
    if (v[i][curr])
        return dp[i][curr];
 
    // Setting the state as solved
    v[i][curr] = 1;
 
    // Recurrence relation
    return dp[i][curr] = findCnt(arr, i + 1,
                                 curr, n, m)
                         + findCnt(arr, i + 1,
                                   (curr + arr[i]) % m,
                                   n, m);
}
 
// Driver code
int main()
{
    int arr[] = { 3, 3, 3, 3 };
    int n = sizeof(arr) / sizeof(int);
    int m = 6;
 
    cout << findCnt(arr, 0, 0, n, m) - 1;
 
    return 0;
}




// Java implementation of the approach
class GFG
{
static int maxN = 20;
static int maxM = 10;
 
// To store the states of DP
static int [][]dp = new int[maxN][maxM];
static boolean [][]v = new boolean[maxN][maxM];
 
// Function to find the required count
static int findCnt(int[] arr, int i,
                   int curr, int n, int m)
{
    // Base case
    if (i == n)
    {
        if (curr == 0)
            return 1;
        else
            return 0;
    }
 
    // If the state has been solved before
    // return the value of the state
    if (v[i][curr])
        return dp[i][curr];
 
    // Setting the state as solved
    v[i][curr] = true;
 
    // Recurrence relation
    return dp[i][curr] = findCnt(arr, i + 1,
                                 curr, n, m) +
                         findCnt(arr, i + 1,
                                (curr + arr[i]) % m,
                                 n, m);
}
 
// Driver code
public static void main(String[] args)
{
    int arr[] = { 3, 3, 3, 3 };
    int n = arr.length;
    int m = 6;
 
    System.out.println(findCnt(arr, 0, 0, n, m) - 1);
}
}
 
// This code is contributed by 29AjayKumar




# Python3 implementation of the approach
maxN = 20
maxM = 10
 
# To store the states of DP
dp = [[0 for i in range(maxN)]
         for i in range(maxM)]
v = [[0 for i in range(maxN)]
        for i in range(maxM)]
 
# Function to find the required count
def findCnt(arr, i, curr, n, m):
     
    # Base case
    if (i == n):
        if (curr == 0):
            return 1
        else:
            return 0
 
    # If the state has been solved before
    # return the value of the state
    if (v[i][curr]):
        return dp[i][curr]
 
    # Setting the state as solved
    v[i][curr] = 1
 
    # Recurrence relation
    dp[i][curr] = findCnt(arr, i + 1,
                          curr, n, m) + \
                  findCnt(arr, i + 1,
                         (curr + arr[i]) % m, n, m)
    return dp[i][curr]
 
# Driver code
arr = [3, 3, 3, 3]
n = len(arr)
m = 6
 
print(findCnt(arr, 0, 0, n, m) - 1)
 
# This code is contributed by Mohit Kumar




// C# implementation of the approach
using System;
 
class GFG
{
    static int maxN = 20;
    static int maxM = 10;
     
    // To store the states of DP
    static int [,]dp = new int[maxN, maxM];
    static bool [,]v = new bool[maxN, maxM];
     
    // Function to find the required count
    static int findCnt(int[] arr, int i,
                       int curr, int n, int m)
    {
        // Base case
        if (i == n)
        {
            if (curr == 0)
                return 1;
            else
                return 0;
        }
     
        // If the state has been solved before
        // return the value of the state
        if (v[i, curr])
            return dp[i, curr];
     
        // Setting the state as solved
        v[i, curr] = true;
     
        // Recurrence relation
        return dp[i, curr] = findCnt(arr, i + 1,
                                     curr, n, m) +
                             findCnt(arr, i + 1,
                                    (curr + arr[i]) % m, n, m);
    }
     
    // Driver code
    public static void Main()
    {
        int []arr = { 3, 3, 3, 3 };
        int n = arr.Length;
        int m = 6;
     
        Console.WriteLine(findCnt(arr, 0, 0, n, m) - 1);
    }
}
 
// This code is contributed by kanugargng




<script>
 
// Javascript implementation of the approach
var maxN = 20
var maxM = 10
 
// To store the states of DP
var dp = Array.from(Array(maxN), () => Array(maxM));
var v = Array.from(Array(maxN), () => Array(maxM));
 
// Function to find the required count
function findCnt(arr, i, curr, n, m)
{
    // Base case
    if (i == n) {
        if (curr == 0)
            return 1;
        else
            return 0;
    }
 
    // If the state has been solved before
    // return the value of the state
    if (v[i][curr])
        return dp[i][curr];
 
    // Setting the state as solved
    v[i][curr] = 1;
 
    // Recurrence relation
    dp[i][curr] = findCnt(arr, i + 1,
                                 curr, n, m)
                         + findCnt(arr, i + 1,
                                   (curr + arr[i]) % m,
                                   n, m);
 
    return dp[i][curr];
}
 
// Driver code
var arr = [3, 3, 3, 3];
var n = arr.length;
var m = 6;
document.write( findCnt(arr, 0, 0, n, m) - 1);
 
</script>

Output
7
















Time Complexity :  O(2^n) as it uses a recursive approach with a branching factor of 2 at each level of recursion. 

Space complexity :  O(n*m) as it uses a 2D array with n rows and m columns to store the states of the DP.

Another 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 :

Implementation :




// C++ code for above approach
 
#include <bits/stdc++.h>
using namespace std;
#define maxN 20
#define maxM 10
 
 
// Function to find the required count
int findCnt(int arr[] , int n , int m){
     
    int dp[n + 1][m];
 
    // Initialize the base case
    for (int j = 0; j < m; j++) {
        dp[n][j] = (j == 0) ? 1 : 0;
    }
 
    // Fill the table using DP
    for (int i = n - 1; i >= 0; i--) {
        for (int j = 0; j < m; j++) {
            dp[i][j] = dp[i + 1][j] + dp[i + 1][(j - arr[i] + m) % m];
        }
    }
 
    // Print the final answer
    return dp[0][0] - 1;
     
     
}
 
//Driver Code
int main()
{
    int arr[] = { 3, 3, 3, 3 };
    int n = sizeof(arr) / sizeof(int);
    int m = 6;
         
    // Function call
    cout << findCnt(arr, n , m);
 
    return 0;
}




// Java code for above approach
 
import java.util.*;
 
public class Main {
  
  // Function to find the required count
  public static int findCnt(int[] arr, int n, int m) {
 
      int[][] dp = new int[n + 1][m];
 
      // Initialize the base case
      for (int j = 0; j < m; j++) {
          dp[n][j] = (j == 0) ? 1 : 0;
      }
 
      // Fill the table using DP
      for (int i = n - 1; i >= 0; i--) {
          for (int j = 0; j < m; j++) {
              dp[i][j] = dp[i + 1][j] + dp[i + 1][(j - arr[i] + m) % m];
          }
      }
 
      // Print the final answer
      return dp[0][0] - 1;
    }
 
    // Driver Code
    public static void main(String[] args) {
        int[] arr = { 3, 3, 3, 3 };
        int n = arr.length;
        int m = 6;
 
        // Function call
        System.out.println(findCnt(arr, n, m));
    }
 
   
}




# Python code for above approach
 
# Function to find the required count
def findCnt(arr, n, m):
 
    dp = [[0 for j in range(m)] for i in range(n + 1)]
 
    # Initialize the base case
    for j in range(m):
        dp[n][j] = 1 if j == 0 else 0
 
    # Fill the table using DP
    for i in range(n - 1, -1, -1):
        for j in range(m):
            dp[i][j] = dp[i + 1][j] + dp[i + 1][(j - arr[i] + m) % m]
 
    # Print the final answer
    return dp[0][0] - 1
 
# Driver Code
arr = [3, 3, 3, 3]
n = len(arr)
m = 6
 
# Function call
print(findCnt(arr, n, m))




// C# code for above approach
 
using System;
 
class GFG {
    const int maxN = 20;
    const int maxM = 10;
 
    // Function to find the required count
    static int FindCnt(int[] arr, int n, int m)
    {
        int[, ] dp = new int[n + 1, m];
 
        // Initialize the base case
        for (int j = 0; j < m; j++) {
            dp[n, j] = (j == 0) ? 1 : 0;
        }
 
        // Fill the table using DP
        for (int i = n - 1; i >= 0; i--) {
            for (int j = 0; j < m; j++) {
                dp[i, j]
                    = dp[i + 1, j]
                      + dp[i + 1, (j - arr[i] + m) % m];
            }
        }
 
        // Print the final answer
        return dp[0, 0] - 1;
    }
 
    // Driver Code
    static void Main()
    {
        int[] arr = { 3, 3, 3, 3 };
        int n = arr.Length;
        int m = 6;
 
        // Function call
        Console.WriteLine(FindCnt(arr, n, m));
    }
}




// Function to find the required count
function findCnt(arr, n, m) {
    let dp = new Array(n + 1);
    for (let i = 0; i < dp.length; i++) {
        dp[i] = new Array(m).fill(0);
    }
 
    // Initialize the base case
    for (let j = 0; j < m; j++) {
        dp[n][j] = j == 0 ? 1 : 0;
    }
 
    // Fill the table using DP
    for (let i = n - 1; i >= 0; i--) {
        for (let j = 0; j < m; j++) {
            dp[i][j] = dp[i + 1][j] + dp[i + 1][(j - arr[i] + m) % m];
        }
    }
 
    // Print the final answer
    return dp[0][0] - 1;
}
 
// Driver Code
let arr = [3, 3, 3, 3];
let n = arr.length;
let m = 6;
 
// Function call
console.log(findCnt(arr, n, m));

Output

7

Time Complexity :  O(n*m)

Auxiliary Space :  O(n*m) 

Efficient approach : Space optimization

In previous approach the current value dp[i][j] is only depend upon the current and previous row values of DP. So to optimize the space complexity we use a single 1D array to store the computations.

Implementation steps:

Implementation: 




// C++ code for above approach
#include <bits/stdc++.h>
using namespace std;
 
// Function to find the required count
int findCnt(int arr[], int n, int m)
{
    int dp[m] = { 0 };
 
    dp[0] = 1;
 
    // Fill the table using DP
    for (int i = 0; i < n; i++) {
        int next_dp[m];
 
        for (int j = 0; j < m; j++) {
            next_dp[j] = dp[j] + dp[(j - arr[i] + m) % m];
        }
 
        memcpy(dp, next_dp, sizeof(next_dp));
    }
 
    // Print the final answer
    return dp[0] - 1;
}
 
// Driver Code
int main()
{
    int arr[] = { 3, 3, 3, 3 };
    int n = sizeof(arr) / sizeof(int);
    int m = 6;
 
    // Function call
    cout << findCnt(arr, n, m);
 
    return 0;
}




import java.util.Arrays;
 
public class GFG {
 
    // Function to find the required count
    static int findCnt(int[] arr, int n, int m)
    {
        int[] dp = new int[m];
        dp[0] = 1;
 
        // Fill the table using DP
        for (int i = 0; i < n; i++) {
            int[] next_dp = new int[m];
 
            for (int j = 0; j < m; j++) {
                next_dp[j]
                    = dp[j] + dp[(j - arr[i] + m) % m];
            }
 
            dp = Arrays.copyOf(next_dp, m);
        }
 
        // Print the final answer
        return dp[0] - 1;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        int[] arr = { 3, 3, 3, 3 };
        int n = arr.length;
        int m = 6;
 
        // Function call
        System.out.println(findCnt(arr, n, m));
    }
}




def findCnt(arr, n, m):
    dp = [0] * m
    dp[0] = 1
 
    # Fill the table using DP
    for i in range(n):
        next_dp = [0] * m
 
        for j in range(m):
            next_dp[j] = dp[j] + dp[(j - arr[i] + m) % m]
 
        dp = next_dp
 
    # Print the final answer
    return dp[0] - 1
 
 
# Driver Code
if __name__ == "__main__":
    arr = [3, 3, 3, 3]
    n = len(arr)
    m = 6
 
    # Function call
    print(findCnt(arr, n, m))




using System;
 
class GFG {
    // Function to find the required count
    static int FindCnt(int[] arr, int n, int m)
    {
        int[] dp = new int[m];
        dp[0] = 1;
 
        // Fill the table using DP
        for (int i = 0; i < n; i++) {
            int[] nextDp = new int[m];
 
            for (int j = 0; j < m; j++) {
                nextDp[j]
                    = dp[j] + dp[(j - arr[i] + m) % m];
            }
 
            Array.Copy(nextDp, dp, m);
        }
 
        // Print the final answer
        return dp[0] - 1;
    }
 
    // Driver Code
    static void Main()
    {
        int[] arr = { 3, 3, 3, 3 };
        int n = arr.Length;
        int m = 6;
 
        // Function call
        Console.WriteLine(FindCnt(arr, n, m));
    }
}




// Function to find the required count
function findCnt(arr, n, m) {
    const dp = new Array(m).fill(0);
 
    dp[0] = 1;
 
    // Fill the table using DP
    for (let i = 0; i < n; i++) {
        const nextDp = new Array(m);
 
        for (let j = 0; j < m; j++) {
            nextDp[j] = dp[j] + dp[(j - arr[i] + m) % m];
        }
 
        dp.splice(0, m, ...nextDp);
    }
 
    // Print the final answer
    return dp[0] - 1;
}
 
// Driver Code
function main() {
    const arr = [3, 3, 3, 3];
    const n = arr.length;
    const m = 6;
 
    // Function call
    console.log(findCnt(arr, n, m));
}
 
main();

Output
7
















Time Complexity :  O(n*m)
Auxiliary Space :  O(m) 

METHOD 4:Using re

APPRAOCH:

The count_subsets function takes an array arr and a number m as inputs. It iterates over all possible subsets of arr using bit manipulation and checks if the sum of each subset is divisible by m. It keeps a count of such subsets and returns the final count

ALGORITHM:

1.Initialize a count variable to 0.
2.Get the length of the array arr and store it in n.
3.Iterate from 1 to 2^n – 1 (representing all possible subsets of arr).
4.For each iteration, generate a subset by checking the bits of the current iteration number i.
5.If the jth bit of i is set ((i >> j) & 1), include the corresponding element arr[j] in the subset.
6.Check if the sum of the subset is divisible by m using the modulo operator.
7.If it is, increment the count.
8.Return the final count.




#include <iostream>
#include <vector>
 
// Function to count subsets with sum divisible by m
int countSubsets(const std::vector<int>& arr, int m)
{
    int count = 0;
    int n = arr.size();
 
    // Iterate through all possible subsets
    for (int i = 1; i < (1 << n); i++) {
        std::vector<int> subset;
 
        // Include elements in the current subset based on
        // the bitmask
        for (int j = 0; j < n; j++) {
            if ((i >> j) & 1) {
                subset.push_back(arr[j]);
            }
        }
 
        // Calculate the sum of the current subset
        int subsetSum = 0;
        for (int element : subset) {
            subsetSum += element;
        }
 
        // Check if the sum is divisible by m
        if (subsetSum % m == 0) {
            count++;
        }
    }
    return count;
}
 
int main()
{
    std::vector<int> arr = { 3, 3, 3, 3 };
    int m = 6;
 
    int result = countSubsets(arr, m);
    std::cout << result << std::endl;
 
    return 0;
}




import java.util.ArrayList;
import java.util.List;
 
public class SubsetsSumDivisibleByM {
    // Function to count subsets with sum divisible by m
    public static int countSubsets(List<Integer> arr, int m)
    {
        int count = 0;
        int n = arr.size();
 
        // Iterate through all possible subsets
        for (int i = 1; i < (1 << n); i++) {
            List<Integer> subset = new ArrayList<>();
 
            // Include elements in the current subset based
            // on the bitmask
            for (int j = 0; j < n; j++) {
                if (((i >> j) & 1) == 1) {
                    subset.add(arr.get(j));
                }
            }
 
            // Calculate the sum of the current subset
            int subsetSum = 0;
            for (int element : subset) {
                subsetSum += element;
            }
 
            // Check if the sum is divisible by m
            if (subsetSum % m == 0) {
                count++;
            }
        }
        return count;
    }
 
    public static void main(String[] args)
    {
        List<Integer> arr = List.of(3, 3, 3, 3);
        int m = 6;
 
        int result = countSubsets(arr, m);
        System.out.println(result);
    }
}




import re
 
def count_subsets(arr, m):
    count = 0
    n = len(arr)
    for i in range(1, 2**n):
        subset = [arr[j] for j in range(n) if (i >> j) & 1]
        if sum(subset) % m == 0:
            count += 1
    return count
 
arr = [3, 3, 3, 3]
m = 6
 
result = count_subsets(arr, m)
print(result)




using System;
using System.Collections.Generic;
 
class Program {
    // Function to count subsets with sum divisible by m
    static int CountSubsets(List<int> arr, int m)
    {
        int count = 0;
        int n = arr.Count;
 
        // Iterate through all possible subsets
        for (int i = 1; i < (1 << n); i++) {
            List<int> subset = new List<int>();
 
            // Include elements in the current subset based
            // on the bitmask
            for (int j = 0; j < n; j++) {
                if ((i & (1 << j)) != 0) {
                    subset.Add(arr[j]);
                }
            }
 
            // Calculate the sum of the current subset
            int subsetSum = 0;
            foreach(int element in subset)
            {
                subsetSum += element;
            }
 
            // Check if the sum is divisible by m
            if (subsetSum % m == 0) {
                count++;
            }
        }
        return count;
    }
 
    static void Main()
    {
        List<int> arr = new List<int>{ 3, 3, 3, 3 };
        int m = 6;
 
        int result = CountSubsets(arr, m);
        Console.WriteLine(result);
    }
}




// Function to count subsets with sum divisible by m
function countSubsets(arr, m) {
    let count = 0;
    const n = arr.length;
 
    // Iterate through all possible subsets
    for (let i = 1; i < (1 << n); i++) {
        const subset = [];
 
        // Include elements in the current subset based
        // on the bitmask
        for (let j = 0; j < n; j++) {
            if (((i >> j) & 1) === 1) {
                subset.push(arr[j]);
            }
        }
 
        // Calculate the sum of the current subset
        const subsetSum = subset.reduce((sum, element) => sum + element, 0);
 
        // Check if the sum is divisible by m
        if (subsetSum % m === 0) {
            count++;
        }
    }
    return count;
}
 
// Main function
function main() {
    const arr = [3, 3, 3, 3];
    const m = 6;
 
    const result = countSubsets(arr, m);
    console.log(result);
}
 
// Call the main function
main();

Output
7
















Time Complexity:
The time complexity of this approach is O(2^n * n), where n is the length of the array arr. Generating all possible subsets using bit manipulation takes O(2^n) time, and for each subset, we calculate the sum in O(n) time.

Auxiliary Space:
The space complexity is O(n), where n is the length of the array arr. We store the generated subset temporarily, and the space required is proportional to the number of elements in the array.


Article Tags :