Given an array of integers arr[] and an integer k, count the number of subsets whose XOR of all elements is equal to k.
Examples :
Input: arr[] = [6, 9, 4, 2], k = 6
Output: 2
Explanation: The subsets having XOR equal to 6 are {6} and {4, 2}.Input: arr[] = [1, 2, 3, 4, 5], k = 4
Output: 4
Explanation: The subsets having XOR equal to 4 are {4}, {1, 5}, {1, 2, 3, 4}, and {2, 3, 5}.
Table of Content
[Naive Approach] Generate All Subsets - O(2^n) Time and O(n) Space
Generate all subsets considering two choices for every element, either include it in the current subset or exclude it. Recursively explore both possibilities while maintaining the XOR of the selected elements. Whenever all elements have been processed, check whether the current XOR is equal to k. If it is, count the subset.
#include <bits/stdc++.h>
using namespace std;
int solve(int i, int currXor, vector<int> &arr, int k) {
if (i == arr.size()) {
return currXor == k;
}
// Exclude current element.
int exclude = solve(i + 1, currXor, arr, k);
// Include current element.
int include = solve(i + 1, currXor ^ arr[i], arr, k);
return exclude + include;
}
int subsetXOR(vector<int> &arr, int k) {
return solve(0, 0, arr, k);
}
int main() {
vector<int> arr = {6, 9, 4, 2};
int k = 6;
cout << subsetXOR(arr, k);
return 0;
}
public class GFG {
static int solve(int i, int currXor, int[] arr, int k) {
if (i == arr.length) {
return currXor == k ? 1 : 0;
}
// Exclude current element.
int exclude = solve(i + 1, currXor, arr, k);
// Include current element.
int include = solve(i + 1, currXor ^ arr[i], arr, k);
return exclude + include;
}
static int subsetXOR(int[] arr, int k) {
return solve(0, 0, arr, k);
}
public static void main(String[] args) {
int[] arr = {6, 9, 4, 2};
int k = 6;
System.out.println(subsetXOR(arr, k));
}
}
def solve(i, curr_xor, arr, k):
if i == len(arr):
return 1 if curr_xor == k else 0
# Exclude current element.
exclude = solve(i + 1, curr_xor, arr, k)
# Include current element.
include = solve(i + 1, curr_xor ^ arr[i], arr, k)
return exclude + include
def subsetXOR(arr, k):
return solve(0, 0, arr, k)
arr = [6, 9, 4, 2]
k = 6
print(subsetXOR(arr, k))
using System;
class GFG
{
static int Solve(int i, int currXor, int[] arr, int k)
{
if (i == arr.Length)
{
return currXor == k ? 1 : 0;
}
// Exclude current element.
int exclude = Solve(i + 1, currXor, arr, k);
// Include current element.
int include = Solve(i + 1, currXor ^ arr[i], arr, k);
return exclude + include;
}
static int subsetXOR(int[] arr, int k)
{
return Solve(0, 0, arr, k);
}
static void Main()
{
int[] arr = { 6, 9, 4, 2 };
int k = 6;
Console.WriteLine(subsetXOR(arr, k));
}
}
function solve(i, currXor, arr, k) {
if (i === arr.length) {
return currXor === k ? 1 : 0;
}
// Exclude current element.
const exclude = solve(i + 1, currXor, arr, k);
// Include current element.
const include = solve(i + 1, currXor ^ arr[i], arr, k);
return exclude + include;
}
function subsetXOR(arr, k) {
return solve(0, 0, arr, k);
}
// Driver Code
const arr = [6, 9, 4, 2];
const k = 6;
console.log(subsetXOR(arr, k));
Output
2
[Better Approach] Using Dynamic Programming (Tabulation)
Let dp[i][x] represent the number of subsets formed using the first i elements whose XOR value is x. For each element, we have two choices: exclude it and keep the XOR unchanged, or include it and update the XOR. By filling the table row by row, we can compute the answer without recursion.
#include <bits/stdc++.h>
using namespace std;
int subsetXOR(vector<int> &arr, int k) {
const int MAX_XOR = 128;
int n = arr.size();
vector<vector<int>> dp(n + 1,
vector<int>(MAX_XOR, 0));
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int x = 0; x < MAX_XOR; x++) {
dp[i][x] = dp[i - 1][x]
+ dp[i - 1][x ^ arr[i - 1]];
}
}
return dp[n][k];
}
int main() {
vector<int> arr = {6, 9, 4, 2};
int k = 6;
cout << subsetXOR(arr, k);
return 0;
}
public class GFG {
static int subsetXOR(int[] arr, int k) {
final int MAX_XOR = 128;
int n = arr.length;
int[][] dp = new int[n + 1][MAX_XOR];
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int x = 0; x < MAX_XOR; x++) {
dp[i][x] = dp[i - 1][x]
+ dp[i - 1][x ^ arr[i - 1]];
}
}
return dp[n][k];
}
public static void main(String[] args) {
int[] arr = {6, 9, 4, 2};
int k = 6;
System.out.println(subsetXOR(arr, k));
}
}
def subsetXOR(arr, k):
MAX_XOR = 128
n = len(arr)
dp = [[0] * MAX_XOR for _ in range(n + 1)]
dp[0][0] = 1
for i in range(1, n + 1):
for x in range(MAX_XOR):
dp[i][x] = dp[i - 1][x] + dp[i - 1][x ^ arr[i - 1]]
return dp[n][k]
arr = [6, 9, 4, 2]
k = 6
print(subsetXOR(arr, k))
using System;
class GFG
{
static int subsetXOR(int[] arr, int k)
{
const int MAX_XOR = 128;
int n = arr.Length;
int[,] dp = new int[n + 1, MAX_XOR];
dp[0, 0] = 1;
for (int i = 1; i <= n; i++)
{
for (int x = 0; x < MAX_XOR; x++)
{
dp[i, x] = dp[i - 1, x]
+ dp[i - 1, x ^ arr[i - 1]];
}
}
return dp[n, k];
}
static void Main()
{
int[] arr = { 6, 9, 4, 2 };
int k = 6;
Console.WriteLine(subsetXOR(arr, k));
}
}
function subsetXOR(arr, k) {
const MAX_XOR = 128;
const n = arr.length;
const dp = Array.from(
{ length: n + 1 },
() => Array(MAX_XOR).fill(0)
);
dp[0][0] = 1;
for (let i = 1; i <= n; i++) {
for (let x = 0; x < MAX_XOR; x++) {
dp[i][x] = dp[i - 1][x]
+ dp[i - 1][x ^ arr[i - 1]];
}
}
return dp[n][k];
}
// Driver Code
const arr = [6, 9, 4, 2];
const k = 6;
console.log(subsetXOR(arr, k));
Output
2
Time Complexity: O(n * max_xor) - There are n * max_xor states, and each state is computed once.
Auxiliary Space: O(n * max_xor) - A DP table of size n * max_xor is used.
[Expected Approach] Space Optimized Dynamic Programming
In the tabulation approach, dp[i][x] depends only on the previous row dp[i - 1][]. Therefore, instead of storing all n rows, we can keep only the counts for the previous state. For each element, create a new DP array representing the updated counts after considering that element. This reduces the space complexity from O(n * max_xor) to O(max_xor) while maintaining the same time complexity.
#include <bits/stdc++.h>
using namespace std;
int subsetXOR(vector<int> &arr, int k) {
const int MAX_XOR = 128;
vector<int> dp(MAX_XOR, 0);
dp[0] = 1;
for (int num : arr) {
vector<int> newDp(MAX_XOR, 0);
for (int x = 0; x < MAX_XOR; x++) {
// Exclude current element.
newDp[x] += dp[x];
// Include current element.
newDp[x ^ num] += dp[x];
}
dp = move(newDp);
}
return dp[k];
}
int main() {
vector<int> arr = {6, 9, 4, 2};
int k = 6;
cout << subsetXOR(arr, k);
return 0;
}
public class GFG {
static int subsetXOR(int[] arr, int k) {
final int MAX_XOR = 128;
int[] dp = new int[MAX_XOR];
dp[0] = 1;
for (int num : arr) {
int[] newDp = new int[MAX_XOR];
for (int x = 0; x < MAX_XOR; x++) {
// Exclude current element.
newDp[x] += dp[x];
// Include current element.
newDp[x ^ num] += dp[x];
}
dp = newDp;
}
return dp[k];
}
public static void main(String[] args) {
int[] arr = {6, 9, 4, 2};
int k = 6;
System.out.println(subsetXOR(arr, k));
}
}
def subsetXOR(arr, k):
MAX_XOR = 128
dp = [0] * MAX_XOR
dp[0] = 1
for num in arr:
new_dp = [0] * MAX_XOR
for x in range(MAX_XOR):
# Exclude current element.
new_dp[x] += dp[x]
# Include current element.
new_dp[x ^ num] += dp[x]
dp = new_dp
return dp[k]
arr = [6, 9, 4, 2]
k = 6
print(subsetXOR(arr, k))
using System;
class GFG
{
static int subsetXOR(int[] arr, int k)
{
const int MAX_XOR = 128;
int[] dp = new int[MAX_XOR];
dp[0] = 1;
foreach (int num in arr)
{
int[] newDp = new int[MAX_XOR];
for (int x = 0; x < MAX_XOR; x++)
{
// Exclude current element.
newDp[x] += dp[x];
// Include current element.
newDp[x ^ num] += dp[x];
}
dp = newDp;
}
return dp[k];
}
static void Main()
{
int[] arr = { 6, 9, 4, 2 };
int k = 6;
Console.WriteLine(subsetXOR(arr, k));
}
}
function subsetXOR(arr, k) {
const MAX_XOR = 128;
let dp = new Array(MAX_XOR).fill(0);
dp[0] = 1;
for (const num of arr) {
const newDp = new Array(MAX_XOR).fill(0);
for (let x = 0; x < MAX_XOR; x++) {
// Exclude current element.
newDp[x] += dp[x];
// Include current element.
newDp[x ^ num] += dp[x];
}
dp = newDp;
}
return dp[k];
}
// Driver Code
const arr = [6, 9, 4, 2];
const k = 6;
console.log(subsetXOR(arr, k));
Output
2
Time Complexity: O(n * max_xor) - For each element, all possible XOR values are processed once.
Auxiliary Space: O(max_xor) - Only two arrays of size max_xor are used.