Longest subsequence whose sum is divisible by a given number
Last Updated :
23 Dec, 2023
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>
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));
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();
Time Complexity: O(N * M)
Auxiliary Space: O(M + M), as we use two M size arrays.
Similar Reads
Subsequences of size three in an array whose sum is divisible by m Given an array A[] (1<=A_i <=10^9 ) of size N (1<=N<=10^5 ), find the number of subsequences of length 3 whose sum is divisible by M (1<=M<=10^3 ). Examples: Input : A[] = {1, 2, 4, 3} M = 3 Output : 2 Explanation : We can choose two such subsequence of length 3 such that its sum i
15+ min read
Longest Subsequence from a numeric String divisible by K Given an integer K and a numeric string str, the task is to find the longest subsequence from the given string which is divisible by K. Examples: Input: str = "121400", K = 8Output: 121400Explanation:Since the whole string is divisible by 8, the entire string is the answer. Input: str: "7675437", K
8 min read
Sum of n digit numbers divisible by a given number Given n and a number, the task is to find the sum of n digit numbers that are divisible by given number.Examples: Input : n = 2, number = 7Output : 728Explanation: There are thirteen n digit numbers that are divisible by 7. Numbers are : 14+ 21 + 28 + 35 + 42 + 49 + 56 + 63 +70 + 77 + 84 + 91 + 98.
9 min read
Find the maximum range [L,R] whose sum is divisible by M Given an array arr[] consisting of positive numbers, the task is to find the maximum range [L, R] whose sum is divisible by M. If there is no range present return -1. Examples: Input: arr[] = {3, 7, 5, 2, 5, 10}, M = 3 Output: 1 3 Explanation: Sum of numbers from 1 to 3 is 3+7+5 which is 15. Input :
8 min read
Longest Subarray With Sum Divisible By K Given an arr[] containing n integers and a positive integer k, he problem is to find the longest subarray's length with the sum of the elements divisible by k.Examples:Input: arr[] = [2, 7, 6, 1, 4, 5], k = 3Output: 4Explanation: The subarray [7, 6, 1, 4] has sum = 18, which is divisible by 3.Input:
10 min read