Given an array arr[] and an integer M, the task is to find the length of the longest subsequence whose sum is divisible by M. If there is no such sub-sequence then print 0.
Examples:
Input: arr[] = {3, 2, 2, 1}, M = 3
Output: 3
Longest sub-sequence whose sum is
divisible by 3 is {3, 2, 1}
Input: arr[] = {2, 2}, M = 3
Output: 0
Approach: A simple way to solve this will be to generate all the possible sub-sequences and then find the largest among them divisible whose sum is divisible by M. However, for smaller values of M, a dynamic programming based approach can be used.
Let’s look at the recurrence relation first.
dp[i][curr_mod] = max(dp[i + 1][curr_mod], dp[i + 1][(curr_mod + arr[i]) % m] + 1)
Let’s understand the states of DP now. Here, dp[i][curr_mod] stores the longest subsequence of subarray arr[i…N-1] such that the sum of this subsequence and curr_mod is divisible by M. At each step, either index i can be chosen updating curr_mod or it can be ignored.
Also, note that only SUM % m needs to be stored instead of the entire sum as this information is sufficient to complete the states of DP.
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 64 // To store the states of DP int dp[maxN][maxM]; bool v[maxN][maxM]; // Function to return the length // of the longest subsequence // whose sum is divisible by m int findLen( int * arr, int i, int curr, int n, int m) { // Base case if (i == n) { if (!curr) return 0; else return -1; } // 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 int l = findLen(arr, i + 1, curr, n, m); int r = findLen(arr, i + 1, (curr + arr[i]) % m, n, m); dp[i][curr] = l; if (r != -1) dp[i][curr] = max(dp[i][curr], r + 1); return dp[i][curr]; } // Driver code int main() { int arr[] = { 3, 2, 2, 1 }; int n = sizeof (arr) / sizeof ( int ); int m = 3; cout << findLen(arr, 0, 0, n, m); return 0; } |
Java
// Java implementation of the approach class GFG { static int maxN = 20 ; static int maxM = 64 ; // To store the states of DP static int [][]dp = new int [maxN][maxM]; static boolean [][]v = new boolean [maxN][maxM]; // Function to return the length // of the longest subsequence // whose sum is divisible by m static int findLen( int [] arr, int i, int curr, int n, int m) { // Base case if (i == n) { if (curr == 0 ) return 0 ; else return - 1 ; } // 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 int l = findLen(arr, i + 1 , curr, n, m); int r = findLen(arr, i + 1 , (curr + arr[i]) % m, n, m); dp[i][curr] = l; if (r != - 1 ) dp[i][curr] = Math.max(dp[i][curr], r + 1 ); return dp[i][curr]; } // Driver code public static void main(String []args) { int arr[] = { 3 , 2 , 2 , 1 }; int n = arr.length; int m = 3 ; System.out.println(findLen(arr, 0 , 0 , n, m)); } } // This code is contributed by 29AjayKumar |
Python3
# Python3 implementation of the approach import numpy as np maxN = 20 maxM = 64 # To store the states of DP dp = np.zeros((maxN, maxM)); v = np.zeros((maxN, maxM)); # Function to return the length # of the longest subsequence # whose sum is divisible by m def findLen(arr, i, curr, n, m) : # Base case if (i = = n) : if ( not curr) : return 0 ; else : return - 1 ; # 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 l = findLen(arr, i + 1 , curr, n, m); r = findLen(arr, i + 1 , (curr + arr[i]) % m, n, m); dp[i][curr] = l; if (r ! = - 1 ) : dp[i][curr] = max (dp[i][curr], r + 1 ); return dp[i][curr]; # Driver code if __name__ = = "__main__" : arr = [ 3 , 2 , 2 , 1 ]; n = len (arr); m = 3 ; print (findLen(arr, 0 , 0 , n, m)); # This code is contributed by AnkitRai |
C#
// C# implementation of the approach using System; class GFG { static int maxN = 20; static int maxM = 64; // To store the states of DP static int [,]dp = new int [maxN, maxM]; static Boolean [,]v = new Boolean[maxN, maxM]; // Function to return the length // of the longest subsequence // whose sum is divisible by m static int findLen( int [] arr, int i, int curr, int n, int m) { // Base case if (i == n) { if (curr == 0) return 0; else return -1; } // 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 int l = findLen(arr, i + 1, curr, n, m); int r = findLen(arr, i + 1, (curr + arr[i]) % m, n, m); dp[i, curr] = l; if (r != -1) dp[i, curr] = Math.Max(dp[i, curr], r + 1); return dp[i, curr]; } // Driver code public static void Main(String []args) { int []arr = { 3, 2, 2, 1 }; int n = arr.Length; int m = 3; Console.WriteLine(findLen(arr, 0, 0, n, m)); } } // This code is contributed by 29AjayKumar |
Javascript
<script> // Javascript implementation of the approach var maxN = 20 var maxM = 64 // To store the states of DP var dp = Array.from(Array(maxN), ()=> Array(maxM).fill(0)); var v = Array.from(Array(maxN), ()=> Array(maxM).fill( false )); // Function to return the length // of the longest subsequence // whose sum is divisible by m function findLen(arr, i, curr, n, m) { // Base case if (i == n) { if (!curr) return 0; else return -1; } // 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 var l = findLen(arr, i + 1, curr, n, m); var r = findLen(arr, i + 1, (curr + arr[i]) % m, n, m); dp[i][curr] = l; if (r != -1) dp[i][curr] = Math.max(dp[i][curr], r + 1); return dp[i][curr]; } // Driver code var arr = [3, 2, 2, 1]; var n = arr.length; var m = 3; document.write( findLen(arr, 0, 0, n, m)); </script> |
3
Time Complexity: O(N * M)
Auxiliary Space: O(N * M).
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 DP to store the solution of the subproblems.
- Initialize the DP with base cases by initializing the values of DP with 0 and -1.
- 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].
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h> using namespace std; #define maxN 20 #define maxM 64 // Function to return the length of the longest subsequence // whose sum is divisible by m int findLen( int * arr, int n, int m) { // To store the states of DP int dp[n + 1][maxM]; // Base case for ( int curr = 0; curr < m; ++curr) { if (curr == 0) dp[n][curr] = 0; else dp[n][curr] = -1; } // Tabulation for ( int i = n - 1; i >= 0; --i) { for ( int curr = 0; curr < m; ++curr) { // Recurrence relation int l = dp[i + 1][curr]; int r = dp[i + 1][(curr + arr[i]) % m]; dp[i][curr] = l; if (r != -1) dp[i][curr] = max(dp[i][curr], r + 1); } } if (dp[0][0] == -1) return 0; else return dp[0][0]; } // Driver code int main() { int arr[] = { 3, 2, 2, 1 }; int n = sizeof (arr) / sizeof ( int ); int m = 3; // Function call cout << findLen(arr, n, m); return 0; } // -- by bhardwajji |
Java
import java.util.Arrays; public class Main { static final int maxN = 20 ; static final int maxM = 64 ; // Function to return the length of the longest // subsequence whose sum is divisible by m static int findLen( int [] arr, int n, int m) { // To store the states of DP int [][] dp = new int [n + 1 ][maxM]; // Base case for ( int curr = 0 ; curr < m; ++curr) { if (curr == 0 ) dp[n][curr] = 0 ; else dp[n][curr] = - 1 ; } // Tabulation for ( int i = n - 1 ; i >= 0 ; --i) { for ( int curr = 0 ; curr < m; ++curr) { // Recurrence relation int l = dp[i + 1 ][curr]; int r = dp[i + 1 ][(curr + arr[i]) % m]; dp[i][curr] = l; if (r != - 1 ) dp[i][curr] = Math.max(dp[i][curr], r + 1 ); } } if (dp[ 0 ][ 0 ] == - 1 ) return 0 ; else return dp[ 0 ][ 0 ]; } // Driver code public static void main(String[] args) { int [] arr = { 3 , 2 , 2 , 1 }; int n = arr.length; int m = 3 ; // Function call System.out.println(findLen(arr, n, m)); } } |
Python
def find_len(arr, n, m): # Define the maximum values for maxN and maxM maxN = 20 maxM = 64 # To store the states of DP dp = [[ 0 for _ in range (maxM)] for _ in range (n + 1 )] # Base case for curr in range (m): if curr = = 0 : dp[n][curr] = 0 else : dp[n][curr] = - 1 # Tabulation for i in range (n - 1 , - 1 , - 1 ): for curr in range (m): # Recurrence relation l = dp[i + 1 ][curr] r = dp[i + 1 ][(curr + arr[i]) % m] dp[i][curr] = l if r ! = - 1 : dp[i][curr] = max (dp[i][curr], r + 1 ) if dp[ 0 ][ 0 ] = = - 1 : return 0 else : return dp[ 0 ][ 0 ] if __name__ = = "__main__" : arr = [ 3 , 2 , 2 , 1 ] n = len (arr) m = 3 # Function call print (find_len(arr, n, m)) |
C#
using System; class GFG { const int maxN = 20; const int maxM = 64; // Function to return the length of the longest // subsequence whose sum is divisible by m static int FindLen( int [] arr, int n, int m) { // To store the states of DP int [, ] dp = new int [n + 1, maxM]; // Base case for ( int curr = 0; curr < m; ++curr) { if (curr == 0) dp[n, curr] = 0; else dp[n, curr] = -1; } // Tabulation for ( int i = n - 1; i >= 0; --i) { for ( int curr = 0; curr < m; ++curr) { // Recurrence relation int l = dp[i + 1, curr]; int r = dp[i + 1, (curr + arr[i]) % m]; dp[i, curr] = l; if (r != -1) dp[i, curr] = Math.Max(dp[i, curr], r + 1); } } if (dp[0, 0] == -1) return 0; else return dp[0, 0]; } // Driver code static void Main( string [] args) { int [] arr = { 3, 2, 2, 1 }; int n = arr.Length; int m = 3; // Function call Console.WriteLine(FindLen(arr, n, m)); } } |
Javascript
// Function to return the length of the longest subsequence // whose sum is divisible by m function findLen(arr, n, m) { // To store the states of DP const dp = new Array(n + 1); for (let i = 0; i <= n; i++) { dp[i] = new Array(m); } // Base case for (let curr = 0; curr < m; curr++) { if (curr === 0) { dp[n][curr] = 0; } else { dp[n][curr] = -1; } } // Tabulation for (let i = n - 1; i >= 0; i--) { for (let curr = 0; curr < m; curr++) { // Recurrence relation let l = dp[i + 1][curr]; let r = dp[i + 1][(curr + arr[i]) % m]; dp[i][curr] = l; if (r !== -1) { dp[i][curr] = Math.max(dp[i][curr], r + 1); } } } if (dp[0][0] === -1) { return 0; } else { return dp[0][0]; } } // Driver code const arr = [3, 2, 2, 1]; const n = arr.length; const m = 3; // Function call console.log(findLen(arr, n, m)); |
3
Time Complexity: O(N * M)
Auxiliary Space: O(N * M).
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!