Print all subsequences of a string in Python
Given a string s of size n (1 ⤠n ⤠20), the task is to print all subsequences of string. A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.
Examples:
Input: s = "abc"
Output: ["", "a", "b", "c", "ab", "ac", "bc", "abc"]
Explanation: All possible combinations of the characters in the string are returned, including the empty string.
Input: S = "ab"
Output: ["", "a", "b", "ab"]
Explanation: All subsequences of the string ab are returned in any order.
Table of Content
Using Recursion
The recursive approach works by considering two options for each character:
- Include the character in the current subsequence.
- Exclude the character from the current subsequence.
By recursively generating subsequences for all characters, we can obtain the complete set of subsequences.
# Recursive function to generate subsequences.
def subseq_rec(idx, curr, s, res):
# Base case: If we've processed all characters,
# add current subsequence
if idx == len(s):
res.append(curr)
return
# Include current character in subsequence
subseq_rec(idx + 1, curr + s[idx], s, res)
# Exclude current character from subsequence
subseq_rec(idx + 1, curr, s, res)
# Wrapper function to generate all subsequences recursively.
def all_subseq_rec(s):
# List to store results
res = []
# Start recursion from index 0
subseq_rec(0, "", s, res)
return res
s = "abc"
print(all_subseq_rec(s))
Output
['abc', 'ab', 'ac', 'a', 'bc', 'b', 'c', '']
Time Complexity: O(n * 2^n), where n is length of the string. At each step we make two recursive calls resulting in 2^n subsequences.
Auxiliary Space: O(2^n) for storing all subsequences and O(n) recursion stack.
Using Iterative Bit Masking
The iterative approach uses bit masking to generate all subsequences. Each bit in a number represents whether a character is included in the subsequence.
How it Works:
- For a string of length n there are 2^n subsets.
- Each number from 0 to 2^n - 1 represents a subset, where binary representation of number determines which characters are included in the subsequence.
- If the j-th bit is 1 then include the j-th character of string in subsequence.
# Generate subsequences using bit masking.
def all_subseq(s):
n = len(s)
# List to store results
res = []
# Loop over all possible masks from 0 to 2^n - 1
for mask in range(1 << n):
# Current subsequence being formed
curr = ""
# Check each bit in mask
for i in range(n):
# If i-th bit is set, include s[i]
if mask & (1 << i):
curr += s[i]
# Add current subsequence to result list
res.append(curr)
return res
s = "abc"
print(all_subseq(s))
Output
['', 'a', 'b', 'ab', 'c', 'ac', 'bc', 'abc']
Time Complexity: O(n * 2^n), where 2^n is the number of subsequences and n is the maximum length of each subsequence.
Auxiliary Space: O(2^n), as all subsequences are stored in a list.
Using Python's Built-in Libraries (Combinations)
Using itertools.combinations library we can generate all subsequences of lengths 0 to n.
from itertools import combinations
# Generate subsequences using combinations from itertools.
def all_subseq(s):
# Start with the empty subsequence
res = [""]
# Generate combinations of lengths 1 to n
for r in range(1, len(s) + 1):
# Add all combinations of length r to result
res.extend([''.join(comb) for comb in combinations(s, r)])
return res
s = "abc"
print(all_subseq(s))
Output
['', 'a', 'b', 'c', 'ab', 'ac', 'bc', 'abc']
Time Complexity: O(n * 2^n), as it generates all subsets.
Auxiliary Space: O(2^n), as all subsequences are stored.