Open In App

Longest subsequence whose sum is divisible by a given number

Last Updated : 23 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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:
Longest sub-sequence whose sum is 
divisible by 3 is {3, 2, 1}
Input: arr[] = {2, 2}, M = 3 
Output:
 

 

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>


Output

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


Output

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.

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


Java




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


Python3




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


C#




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


Javascript




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


Output

3




Time Complexity: O(N * M)

Auxiliary Space: O(M + M), as we use two M size arrays.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads