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: 7
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> |
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++ 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:
- 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++ 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(); |
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(); |
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.