Given an array arr[] of n positive integers and an integer k, the task is to find the maximum value of the bitwise OR of any subsequence of size k.
Examples:
Input: arr[] = [2, 5, 3, 6, 11, 13], k = 3
Output: 15
Explanation: The sub-sequence with maximum OR value is [2, 11, 13].Input: arr[] = [5, 9, 7, 19], k = 3
Output: 31
Explanation: The sub-sequence with maximum OR value is [5, 9, 19].
Table of Content
Using Recursion - O(2^n) Time and O(n) Space
The idea is to use recursion to explore all possible subsequences of size k. By either including or excluding each element at a given index, we can generate all combinations. The key is to calculate the bitwise OR for each valid subsequence and return the maximum OR value among them.
The recursive relation will be -
- include = maxOrHelper(arr, n, k, index + 1, currOR | arr[index], currSize + 1)
- exclude = maxOrHelper(arr, n, k, index + 1, currOR, currSize);
The final result will me the max(include, exclude).
// C++ implementation to find Max OR of
// subsequence of size k using Recursion
#include <bits/stdc++.h>
using namespace std;
// Helper function to calculate the maximum OR
// of subsequences of size k
int maxOrHelper(vector<int>&arr, int n,
int k, int index, int currOR,
int currSize) {
// If we have selected k elements, return
// the current OR value
if (currSize == k) {
return currOR;
}
// If we have exhausted all elements
if (index == n) {
return 0;
}
// Include the current element and recurse
int include = maxOrHelper(arr, n, k, index + 1,
currOR | arr[index], currSize + 1);
// Skip the current element and recurse
int exclude = maxOrHelper(arr, n, k, index + 1,
currOR, currSize);
// Return the maximum of including or
// excluding the current element
return max(include, exclude);
}
int maxOrK(vector<int>& arr, int k) {
int n = arr.size();
return maxOrHelper(arr, n, k, 0, 0, 0);
}
int main() {
vector<int> arr = {2, 5, 3, 6, 11, 13};
int k = 3;
cout << maxOrK(arr, k);
return 0;
}
// Java implementation to find Max OR of
// subsequence of size k using Recursion
import java.util.*;
class GfG {
// Helper function to calculate the maximum OR
// of subsequences of size k
static int maxOrHelper(List<Integer> arr, int n,
int k, int index, int currOR,
int currSize) {
// If we have selected k elements, return
// the current OR value
if (currSize == k) {
return currOR;
}
// If we have exhausted all elements
if (index == n) {
return 0;
}
// Include the current element and recurse
int include = maxOrHelper(arr, n, k, index + 1,
currOR | arr.get(index), currSize + 1);
// Skip the current element and recurse
int exclude = maxOrHelper(arr, n, k, index + 1,
currOR, currSize);
// Return the maximum of including or
// excluding the current element
return Math.max(include, exclude);
}
static int maxOrK(List<Integer> arr, int k) {
int n = arr.size();
return maxOrHelper(arr, n, k, 0, 0, 0);
}
public static void main(String[] args) {
List<Integer> arr
= Arrays.asList(2, 5, 3, 6, 11, 13);
int k = 3;
System.out.println(maxOrK(arr, k));
}
}
# Python implementation to find Max OR of
# subsequence of size k using Recursion
# Helper function to calculate the maximum OR
# of subsequences of size k
def maxOrHelper(arr, n, k, index, currOR, currSize):
# If we have selected k elements, return
# the current OR value
if currSize == k:
return currOR
# If we have exhausted all elements
if index == n:
return 0
# Include the current element and recurse
include = maxOrHelper(arr, n, k, index + 1,\
currOR | arr[index], currSize + 1)
# Skip the current element and recurse
exclude = maxOrHelper(arr, n, k,\
index + 1, currOR, currSize)
# Return the maximum of including or
# excluding the current element
return max(include, exclude)
def maxOrK(arr, k):
n = len(arr)
return maxOrHelper(arr, n, k, 0, 0, 0)
if __name__ == "__main__":
arr = [2, 5, 3, 6, 11, 13]
k = 3
print(maxOrK(arr, k))
// C# implementation to find Max OR of
// subsequence of size k using Recursion
using System;
using System.Collections.Generic;
class GfG {
// Helper function to calculate the maximum OR
// of subsequences of size k
static int maxOrHelper(List<int> arr, int n,
int k, int index, int currOR,
int currSize) {
// If we have selected k elements, return
// the current OR value
if (currSize == k) {
return currOR;
}
// If we have exhausted all elements
if (index == n) {
return 0;
}
// Include the current element and recurse
int include = maxOrHelper(arr, n, k, index + 1,
currOR | arr[index], currSize + 1);
// Skip the current element and recurse
int exclude = maxOrHelper(arr, n, k, index + 1,
currOR, currSize);
// Return the maximum of including or
// excluding the current element
return Math.Max(include, exclude);
}
static int maxOrK(List<int> arr, int k) {
int n = arr.Count;
return maxOrHelper(arr, n, k, 0, 0, 0);
}
static void Main(string[] args) {
List<int> arr
= new List<int> { 2, 5, 3, 6, 11, 13 };
int k = 3;
Console.WriteLine(maxOrK(arr, k));
}
}
// JavaScript implementation to find Max OR of
// subsequence of size k using Recursion
// Helper function to calculate the maximum OR
// of subsequences of size k
function maxOrHelper(arr, n, k, index,
currOR, currSize) {
// If we have selected k elements, return
// the current OR value
if (currSize === k) {
return currOR;
}
// If we have exhausted all elements
if (index === n) {
return 0;
}
// Include the current element and recurse
let include = maxOrHelper(arr, n, k, index + 1,
currOR | arr[index], currSize + 1);
// Skip the current element and recurse
let exclude = maxOrHelper(arr, n, k, index + 1,
currOR, currSize);
// Return the maximum of including or
// excluding the current element
return Math.max(include, exclude);
}
function maxOrK(arr, k) {
let n = arr.length;
return maxOrHelper(arr, n, k, 0, 0, 0);
}
let arr = [2, 5, 3, 6, 11, 13];
let k = 3;
console.log(maxOrK(arr, k));
Output
15
Using Dynamic Programming - O(n*k*maxOR) Time and O(n*k*maxOR) Space
The idea is to optimize the recursive solution by using memoization to avoid redundant computations. Instead of recalculating the maximum OR value for the same state (defined by the index, current OR value, and size of the subsequence), we store the results in a 3D memoization table with dimensions [n][k+1][maxPossibleOR+1]. and fill it with -1. By doing so, the solution ensures each state is computed only once, significantly reducing the overall computational complexity.
// C++ implementation to find Max OR of
// subsequence of size k using Memoization
#include <bits/stdc++.h>
using namespace std;
// Helper function with memoization
int maxOrHelper(vector<int>& arr, int n, int k,
int index, int currOR, int currSize,
vector<vector<vector<int>>>& memo) {
// If we have selected k elements, return
// the current OR value
if (currSize == k) {
return currOR;
}
// If we have exhausted all elements
if (index == n) {
return 0;
}
// Check if the result is already computed
if (memo[index][currSize][currOR] != -1) {
return memo[index][currSize][currOR];
}
// Include the current element and recurse
int include = maxOrHelper(arr, n, k, index + 1,
currOR | arr[index], currSize + 1, memo);
// Skip the current element and recurse
int exclude = maxOrHelper(arr, n, k, index + 1,
currOR, currSize, memo);
// Store the result in the memoization table
memo[index][currSize][currOR] = max(include, exclude);
return memo[index][currSize][currOR];
}
int MaxOrK(vector<int>& arr, int k) {
int n = arr.size();
// Create a memoization table with dimensions [n][k+1]
// [maximum possible OR value]
int maxPossibleOR = 0;
for (int num : arr) {
maxPossibleOR |= num;
}
// Create a 3D memo table, initializing all values
// as -1
vector<vector<vector<int>>> memo(n + 1,
vector<vector<int>>(k + 1,
vector<int>(maxPossibleOR + 1, -1)));
return maxOrHelper(arr, n, k, 0, 0, 0, memo);
}
int main() {
vector<int> arr = {2, 5, 3, 6, 11, 13};
int k = 3;
cout << MaxOrK(arr, k) << endl;
return 0;
}
// Java implementation to find Max OR of
// subsequence of size k using Memoization
import java.util.*;
class GfG {
// Helper function with memoization
static int maxOrHelper(List<Integer> arr, int n, int k,
int index, int currOR, int currSize,
int[][][] memo) {
// If we have selected k elements, return
// the current OR value
if (currSize == k) {
return currOR;
}
// If we have exhausted all elements
if (index == n) {
return 0;
}
// Check if the result is already computed
if (memo[index][currSize][currOR] != -1) {
return memo[index][currSize][currOR];
}
// Include the current element and recurse
int include = maxOrHelper(arr, n, k, index + 1,
currOR | arr.get(index),
currSize + 1, memo);
// Skip the current element and recurse
int exclude = maxOrHelper(arr, n, k, index + 1,
currOR, currSize, memo);
// Store the result in the memoization table
memo[index][currSize][currOR] = Math.max(include, exclude);
return memo[index][currSize][currOR];
}
static int MaxOrK(List<Integer> arr, int k) {
int n = arr.size();
// Create a memoization table with dimensions [n][k+1]
// [maximum possible OR value]
int maxPossibleOR = 0;
for (int num : arr) {
maxPossibleOR |= num;
}
// Create a 3D memo table, initializing all values
// as -1
int[][][] memo = new int[n + 1][k + 1][maxPossibleOR + 1];
// Initialize the memo table with -1
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++) {
Arrays.fill(memo[i][j], -1);
}
}
return maxOrHelper(arr, n, k, 0, 0, 0, memo);
}
public static void main(String[] args) {
List<Integer> arr = Arrays.asList(2, 5, 3, 6, 11, 13);
int k = 3;
System.out.println(MaxOrK(arr, k));
}
}
# Python implementation to find Max OR of
# subsequence of size k using Memoization
# Helper function with memoization
def maxOrHelper(arr, n, k, index, currOR, currSize, memo):
# If we have selected k elements,
# return the current OR value
if currSize == k:
return currOR
# If we have exhausted all elements
if index == n:
return 0
# Check if the result is already computed
if memo[index][currSize][currOR] != -1:
return memo[index][currSize][currOR]
# Include the current element and recurse
include = maxOrHelper(arr, n, k, index + 1, \
currOR | arr[index], currSize + 1, memo)
# Skip the current element and recurse
exclude = maxOrHelper(arr, n, k, \
index + 1, currOR, currSize, memo)
# Store the result in the memoization table
memo[index][currSize][currOR] = max(include, exclude)
return memo[index][currSize][currOR]
def MaxOrK(arr, k):
n = len(arr)
# Create a memoization table with dimensions [n][k+1]
# [maximum possible OR value]
maxPossibleOR = 0
for num in arr:
maxPossibleOR |= num
# Create a 3D memo table, initializing all values
# as -1
memo = [[[ -1 for i in range(maxPossibleOR + 1)] \
for i in range(k + 1)] for i in range(n + 1)]
return maxOrHelper(arr, n, k, 0, 0, 0, memo)
if __name__ == "__main__":
arr = [2, 5, 3, 6, 11, 13]
k = 3
print(MaxOrK(arr, k))
// C# implementation to find Max OR of
// subsequence of size k using Memoization
using System;
using System.Collections.Generic;
class GfG {
// Helper function with memoization
static int MaxOrHelper(List<int> arr, int n, int k,
int index, int currOR, int currSize,
int[,,] memo) {
// If we have selected k elements, return
// the current OR value
if (currSize == k) {
return currOR;
}
// If we have exhausted all elements
if (index == n) {
return 0;
}
// Check if the result is already computed
if (memo[index, currSize, currOR] != -1) {
return memo[index, currSize, currOR];
}
// Include the current element and recurse
int include = MaxOrHelper(arr, n, k, index + 1,
currOR | arr[index],
currSize + 1, memo);
// Skip the current element and recurse
int exclude = MaxOrHelper(arr, n, k, index + 1,
currOR, currSize, memo);
// Store the result in the memoization table
memo[index, currSize, currOR] = Math.Max(include, exclude);
return memo[index, currSize, currOR];
}
static int MaxOrK(List<int> arr, int k) {
int n = arr.Count;
// Create a memoization table with dimensions [n][k+1]
// [maximum possible OR value]
int maxPossibleOR = 0;
foreach (int num in arr) {
maxPossibleOR |= num;
}
// Create a 3D memo table, initializing all values
// as -1
int[,,] memo = new int[n + 1, k + 1, maxPossibleOR + 1];
// Initialize the memo table with -1
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++) {
for (int l = 0; l <= maxPossibleOR; l++) {
memo[i, j, l] = -1;
}
}
}
return MaxOrHelper(arr, n, k, 0, 0, 0, memo);
}
static void Main() {
List<int> arr = new List<int> { 2, 5, 3, 6, 11, 13 };
int k = 3;
Console.WriteLine(MaxOrK(arr, k));
}
}
// Javascript implementation to find Max OR of
// subsequence of size k using Memoization
// Helper function with memoization
function maxOrHelper(arr, n, k, index, currOR,
currSize, memo) {
// If we have selected k elements,
// return the current OR value
if (currSize === k) {
return currOR;
}
// If we have exhausted all elements
if (index === n) {
return 0;
}
// Check if the result is already computed
if (memo[index][currSize][currOR] !== -1) {
return memo[index][currSize][currOR];
}
// Include the current element and recurse
let include = maxOrHelper(arr, n, k, index + 1,
currOR | arr[index], currSize + 1, memo);
// Skip the current element and recurse
let exclude = maxOrHelper(arr, n, k,
index + 1, currOR, currSize, memo);
// Store the result in the memoization table
memo[index][currSize][currOR] = Math.max(include, exclude);
return memo[index][currSize][currOR];
}
function MaxOrK(arr, k) {
let n = arr.length;
// Create a memoization table with dimensions [n][k+1]
// [maximum possible OR value]
let maxPossibleOR = 0;
for (let num of arr) {
maxPossibleOR |= num;
}
// Create a 3D memo table, initializing all values
// as -1
let memo = Array.from({ length: n + 1 }, () =>
Array.from({ length: k + 1 }, () =>
Array(maxPossibleOR + 1).fill(-1)
)
);
return maxOrHelper(arr, n, k, 0, 0, 0, memo);
}
let arr = [2, 5, 3, 6, 11, 13];
let k = 3;
console.log(MaxOrK(arr, k));
Output
15
Related article: