Tuesday, November 19, 2024
Google search engine
HomeData Modelling & AINumber of subsets with sum divisible by M | Set 2

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++




// 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




// 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




# 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#




// 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


Javascript




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

  • Create a table DP of size n*m to store the solution of the subproblems.
  • Initialize the table DP 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[0][0] – 1

Implementation :

C++




// 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




// 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));
    }
 
   
}


Python3




# 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#




// 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));
    }
}


Javascript




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

  • Create a 1D vector dp of size m.
  • Set a base case by initializing the value of DP ( dp[0] = 1 ).
  • Now iterate over subproblems by the help of nested loop and get the current value from previous computations.
  • Now create a temporary array next_dp to store the current values from previous computations.
  • After every iteration assign the value of next_dp to dp for further iteration.
  • At last return and print the final answer stored in dp[0]-1.

Implementation: 

C++




// 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;
}


Java




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));
    }
}


Python




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))


C#




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));
    }
}


Javascript




// 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.

Python3




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)


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.

Feeling lost in the world of random DSA topics, wasting time without progress? It’s time for a change! Join our DSA course, where we’ll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!

RELATED ARTICLES

Most Popular

Recent Comments