Subset enumeration is one of the cleanest applications of Bit Manipulation.
Every mask is a subset. Decode 101 → {a,c} step by step:
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Enumerate Subsets via Bitmask
List every subset of n items by counting from 0 to 2ⁿ−1.
Bit i of the loop counter decides whether items[i] is in the current subset. Counting through all n-bit integers yields every subset exactly once — no recursion, no call stack. O(n·2ⁿ) time, O(1) extra space per mask.
1
n = len(items)
2
for mask in 0 .. (1 << n) - 1:
3
subset = []
4
for i in 0..n-1:
5
if mask & (1 << i):
6
subset.add(items[i])
7
process(subset)
Instead of backtracking, we use a simple idea:
Every subset can be represented by a binary number.
Mental Trigger
n elements → 2ⁿ subsets → binary numbers from 0 to
(1 << n) - 1
Each bit tells you:
- 1 → include element
- 0 → exclude element
Core Idea
If we have:
arr = [a, b, c]
Then:
000 → {}
001 → {c}
010 → {b}
011 → {b, c}
100 → {a}
...
111 → {a, b, c}
Pattern Table
| Concept | Meaning |
|---|---|
| bit = 1 | include element |
| bit = 0 | skip element |
| i-th bit | arr[i] |
| range | 0 → (1 << n) - 1 |
1. Generic Subset Enumeration Template
Java Code
public List<List<Integer>> generateSubsets(int[] arr) {
int n = arr.length;
List<List<Integer>> result = new ArrayList<>();
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
List<Integer> subset = new ArrayList<>();
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
subset.add(arr[i]);
}
}
result.add(subset);
}
return result;
}def generate_subsets(arr):
n = len(arr)
result = []
total = 1 << n
for mask in range(total):
subset = []
for i in range(n):
if mask & (1 << i):
subset.append(arr[i])
result.append(subset)
return resultvector<vector<int>> generateSubsets(vector<int>& arr) {
int n = arr.size();
vector<vector<int>> result;
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
vector<int> subset;
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
subset.push_back(arr[i]);
}
}
result.push_back(subset);
}
return result;
}function generateSubsets(arr) {
const n = arr.length;
const result = [];
const total = 1 << n;
for (let mask = 0; mask < total; mask++) {
const subset = [];
for (let i = 0; i < n; i++) {
if ((mask & (1 << i)) !== 0) {
subset.push(arr[i]);
}
}
result.push(subset);
}
return result;
}What Changed from Bit Basics?
We combine two ideas:
1. Loop over all masks
Base idea:
1 << n
Now:
for (int mask = 0; mask < (1 << n); mask++)
because each number is a subset.
2. Check each bit per subset
Base pattern:
(mask & (1 << i)) != 0
meaning:
“Is element i included?”
Subsets = all binary numbers from 0 to 2ⁿ - 1
2. Optimized Bit Iteration (Faster Inner Loop)
Instead of scanning all bits, we iterate only set bits.
Java Code
public List<List<Integer>> generateSubsetsOptimized(int[] arr) {
int n = arr.length;
List<List<Integer>> result = new ArrayList<>();
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
List<Integer> subset = new ArrayList<>();
int temp = mask;
while (temp > 0) {
int bit = temp & -temp; // lowest set bit
int idx = Integer.numberOfTrailingZeros(bit);
subset.add(arr[idx]);
temp &= (temp - 1); // remove lowest set bit
}
result.add(subset);
}
return result;
}def generate_subsets_optimized(arr):
n = len(arr)
result = []
total = 1 << n
for mask in range(total):
subset = []
temp = mask
while temp > 0:
bit = temp & -temp # lowest set bit
idx = bit.bit_length() - 1
subset.append(arr[idx])
temp &= temp - 1 # remove lowest set bit
result.append(subset)
return resultvector<vector<int>> generateSubsetsOptimized(vector<int>& arr) {
int n = arr.size();
vector<vector<int>> result;
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
vector<int> subset;
int temp = mask;
while (temp > 0) {
int bit = temp & -temp; // lowest set bit
int idx = __builtin_ctz(bit);
subset.push_back(arr[idx]);
temp &= (temp - 1); // remove lowest set bit
}
result.push_back(subset);
}
return result;
}function generateSubsetsOptimized(arr) {
const n = arr.length;
const result = [];
const total = 1 << n;
for (let mask = 0; mask < total; mask++) {
const subset = [];
let temp = mask;
while (temp > 0) {
const bit = temp & -temp; // lowest set bit
const idx = Math.log2(bit);
subset.push(arr[idx]);
temp &= temp - 1; // remove lowest set bit
}
result.push(subset);
}
return result;
}What Changed?
Instead of:
for (int i = 0; i < n; i++)
We do:
while (temp > 0)
because we only process active bits.
Key Trick
temp & -temp
extracts lowest set bit
Only iterate over selected elements instead of full array
3. Fixed-size Subsets (k elements)
Java Code
public List<List<Integer>> kSizedSubsets(int[] arr, int k) {
int n = arr.length;
List<List<Integer>> result = new ArrayList<>();
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
if (Integer.bitCount(mask) != k) continue;
List<Integer> subset = new ArrayList<>();
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
subset.add(arr[i]);
}
}
result.add(subset);
}
return result;
}def k_sized_subsets(arr, k):
n = len(arr)
result = []
total = 1 << n
for mask in range(total):
if bin(mask).count('1') != k:
continue
subset = []
for i in range(n):
if mask & (1 << i):
subset.append(arr[i])
result.append(subset)
return resultvector<vector<int>> kSizedSubsets(vector<int>& arr, int k) {
int n = arr.size();
vector<vector<int>> result;
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
if (__builtin_popcount(mask) != k) continue;
vector<int> subset;
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
subset.push_back(arr[i]);
}
}
result.push_back(subset);
}
return result;
}function kSizedSubsets(arr, k) {
const n = arr.length;
const result = [];
const total = 1 << n;
for (let mask = 0; mask < total; mask++) {
if (mask.toString(2).replaceAll('0', '').length !== k) continue;
const subset = [];
for (let i = 0; i < n; i++) {
if ((mask & (1 << i)) !== 0) {
subset.push(arr[i]);
}
}
result.push(subset);
}
return result;
}What Changed?
Added constraint:
Integer.bitCount(mask) != k
because we only want subsets of size k.
Bit count = subset size
4. Subsets of String Characters
Java Code
public List<String> stringSubsets(String s) {
int n = s.length();
List<String> result = new ArrayList<>();
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
sb.append(s.charAt(i));
}
}
result.add(sb.toString());
}
return result;
}def string_subsets(s):
n = len(s)
result = []
total = 1 << n
for mask in range(total):
subset = []
for i in range(n):
if mask & (1 << i):
subset.append(s[i])
result.append(''.join(subset))
return resultvector<string> stringSubsets(string s) {
int n = s.size();
vector<string> result;
int total = 1 << n;
for (int mask = 0; mask < total; mask++) {
string sub;
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
sub += s[i];
}
}
result.push_back(sub);
}
return result;
}function stringSubsets(s) {
const n = s.length;
const result = [];
const total = 1 << n;
for (let mask = 0; mask < total; mask++) {
let sub = '';
for (let i = 0; i < n; i++) {
if ((mask & (1 << i)) !== 0) {
sub += s[i];
}
}
result.push(sub);
}
return result;
}Subset generation works for arrays, strings, and even grids
Pattern Evolution
Bit Basics
↓
Mask = 1 << i
↓
Check bits in number
↓
Generate all masks (0 → 2ⁿ - 1)
↓
Build subsets using active bits
Common Mistakes
Using i < n inside wrong loop
for (int mask = 0; mask < n; mask++) // wrong
Correct:
for (int mask = 0; mask < (1 << n); mask++)
Forgetting bit check
subset.add(arr[i]); // wrong (adds everything)
Wrong condition
if ((mask & (1 << i)) == 1) // wrong
Correct:
!= 0
Recognition Cheat Sheet
| If you see… | Think… |
|---|---|
| “all subsets” | 0 → 2ⁿ - 1 |
| “include/exclude choices” | bitmask |
| “fixed k elements” | bitCount |
| “string combinations” | bit iteration |
Premium Content
Unlock Subset Enumeration and all premium lessons with a subscription.
From ₹199.99/year — See plans