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++ 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 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 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# 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 |
<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:
#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 |
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));
}
} |
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))
|
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));
}
} |
// 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).
Space Optimization Approach: From tabulation, we can observe that we only require previous computed result to compute the current dp values. So, we can only maintain two 1D arrays instead of a 2D array. The whole logic of the code is same, but we can optimize the space complexity.
Below is the implementation of the above approach.
#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
vector< int > dp(maxM),new_dp(maxM);
// Initialize the DP array
for ( int curr = 0; curr < m; ++curr) {
dp[curr] = -1;
}
dp[0] = 0;
// Tabulation with space optimization
for ( int i = n-1; i >=0; --i) {
// compute new_dp from prev dp
new_dp=dp;
for ( int curr = 0; curr < m; ++curr) {
// Recurrence relation
int l = dp[curr]; // not pick
int r = dp[(curr - arr[i] + m) % m]; // pick
new_dp[curr] = l;
if (r != -1)
new_dp[curr] = max(new_dp[curr], r + 1);
}
// use new_dp as prev dp
// to compute next states
dp=new_dp;
}
if (dp[0] == -1)
return 0;
else
return dp[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;
} |
public class LongestSubsequence {
// Constants
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 [maxM];
int [] newDp = new int [maxM];
// Initialize the DP array
for ( int curr = 0 ; curr < m; curr++) {
dp[curr] = - 1 ;
}
dp[ 0 ] = 0 ;
// Tabulation with space optimization
for ( int i = n - 1 ; i >= 0 ; i--) {
// compute newDp from prev dp
System.arraycopy(dp, 0 , newDp, 0 , m);
for ( int curr = 0 ; curr < m; curr++) {
// Recurrence relation
int l = dp[curr]; // not pick
int r = dp[(curr - arr[i] + m) % m]; // pick
newDp[curr] = l;
if (r != - 1 ) {
newDp[curr]
= Math.max(newDp[curr], r + 1 );
}
}
// use newDp as prev dp
// to compute next states
System.arraycopy(newDp, 0 , dp, 0 , m);
}
if (dp[ 0 ] == - 1 ) {
return 0 ;
}
else {
return dp[ 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));
}
} |
maxN = 20
maxM = 64
# Function to return the length of the longest subsequence # whose sum is divisible by m def findLen(arr, n, m):
# To store the states of DP
dp = [ - 1 ] * maxM
new_dp = [ 0 ] * maxM
# Initialize the DP array
for curr in range (m):
dp[curr] = - 1
dp[ 0 ] = 0
# Tabulation with space optimization
for i in range (n - 1 , - 1 , - 1 ):
# compute new_dp from prev dp
new_dp = dp.copy()
for curr in range (m):
# Recurrence relation
l = dp[curr] # not pick
r = dp[(curr - arr[i] + m) % m] # pick
new_dp[curr] = l
if r ! = - 1 :
new_dp[curr] = max (new_dp[curr], r + 1 )
# use new_dp as prev dp
# to compute next states
dp = new_dp.copy()
if dp[ 0 ] = = - 1 :
return 0
else :
return dp[ 0 ]
# Driver code if __name__ = = "__main__" :
arr = [ 3 , 2 , 2 , 1 ]
n = len (arr)
m = 3
# Function call
print (findLen(arr, n, m))
|
using System;
class Program {
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 [maxM];
int [] newDp = new int [maxM];
// Initialize the DP array
for ( int curr = 0; curr < m; ++curr) {
dp[curr] = -1;
}
dp[0] = 0;
// Tabulation with space optimization
for ( int i = n - 1; i >= 0; --i) {
// compute newDp from prev dp
Array.Copy(dp, newDp, maxM);
for ( int curr = 0; curr < m; ++curr) {
// Recurrence relation
int l = dp[curr]; // not pick
int r = dp[(curr - arr[i] + m) % m]; // pick
newDp[curr] = l;
if (r != -1)
newDp[curr]
= Math.Max(newDp[curr], r + 1);
}
// use newDp as prev dp
// to compute next states
Array.Copy(newDp, dp, maxM);
}
if (dp[0] == -1)
return 0;
else
return dp[0];
}
// Driver code
static void Main()
{
int [] arr = { 3, 2, 2, 1 };
int n = arr.Length;
int m = 3;
// Function call
Console.WriteLine(FindLen(arr, n, m));
}
} |
function GFG(arr, n, m) {
// To store the states of DP
let dp = new Array(m).fill(-1);
let new_dp = new Array(m);
// Initialize the DP array
dp[0] = 0;
// Tabulation with space optimization
for (let i = n - 1; i >= 0; --i) {
// Compute new_dp from prev dp
new_dp = [...dp];
for (let curr = 0; curr < m; ++curr) {
let l = dp[curr];
let r = dp[(curr - arr[i] + m) % m];
new_dp[curr] = l;
if (r !== -1) {
new_dp[curr] = Math.max(new_dp[curr], r + 1);
}
}
// Use new_dp as prev dp to the compute next states
dp = [...new_dp];
}
if (dp[0] === -1) {
return 0;
} else {
return dp[0];
}
} // Driver code function main() {
const arr = [3, 2, 2, 1];
const n = arr.length;
const m = 3;
// Function call
console.log(GFG(arr, n, m));
} // Execute the main function main(); |
3
Time Complexity: O(N * M)
Auxiliary Space: O(M + M), as we use two M size arrays.