
Problem Statement
The task is to design a CombinationIterator class that efficiently generates combinations of characters from a provided string. The constructor of the class takes two parameters:
- A string
charactersconsisting of sorted, distinct lowercase English letters. - An integer
combinationLength, which defines the length of each combination to generate.
The class should support two methods:
next(): Returns the next combination of the specified length in lexicographical order.hasNext(): Returnstrueif there is another combination available, otherwisefalse.
Examples
Example 1
Input:
["CombinationIterator", "next", "hasNext", "next", "hasNext", "next", "hasNext"] [["abc", 2], [], [], [], [], [], []]
Output:
[null, "ab", true, "ac", true, "bc", false]
Explanation:
CombinationIterator itr = new CombinationIterator("abc", 2);
itr.next(); // returns "ab"
itr.hasNext(); // returns true
itr.next(); // returns "ac"
itr.hasNext(); // returns true
itr.next(); // returns "bc"
itr.hasNext(); // returns falseConstraints
1 <= combinationLength <= characters.length <= 15- All characters in
charactersare unique. - At most
10^4calls will be made tonext()andhasNext(). - All calls to
next()are guaranteed to be valid.
Approach and Intuition
To efficiently generate and manage combinations, the implementation can follow this strategy:
Precompute All Combinations:
- Use a backtracking algorithm to generate all combinations of length
combinationLengthfrom the sorted stringcharacters. - Since the characters are given in lexicographical order, the generated combinations will also follow that order naturally.
- Use a backtracking algorithm to generate all combinations of length
Constructor Logic:
- Store the generated combinations in a list.
- Initialize a pointer or index to keep track of the current position in the list.
next():
- Return the current combination and advance the pointer.
hasNext():
- Check if the pointer is still within bounds of the list of combinations.
This approach is feasible given the constraint that characters.length <= 15. The total number of combinations is manageable (C(15, k) for any valid k), making it practical to precompute all values upfront for O(1) retrieval during iteration.
Using precomputation ensures that next() and hasNext() both run in constant time, satisfying the performance requirements.
Solutions
- Java
- Python
class CombinationGenerator {
int[] sequenceIndices;
boolean moreCombinations;
int length, combinationSize;
String sequence;
public CombinationGenerator(String sequenceInput, int combSize) {
length = sequenceInput.length();
combinationSize = combSize;
sequence = sequenceInput;
moreCombinations = true;
sequenceIndices = new int[combinationSize];
for (int idx = 0; idx < combinationSize; ++idx) {
sequenceIndices[idx] = idx;
}
}
public String next() {
StringBuilder currentCombination = new StringBuilder();
for (int index : sequenceIndices) {
currentCombination.append(sequence.charAt(index));
}
int idx = combinationSize - 1;
while (idx >= 0 && sequenceIndices[idx] == length - combinationSize + idx) {
idx--;
}
if (idx >= 0) {
sequenceIndices[idx]++;
for (int j = idx + 1; j < combinationSize; j++) {
sequenceIndices[j] = sequenceIndices[idx] + j - idx;
}
} else {
moreCombinations = false;
}
return currentCombination.toString();
}
public boolean hasNext() {
return moreCombinations;
}
}
The Java class CombinationGenerator provides a mechanism to iterate over combinations of characters from a given string. It allows determining all possible combinations of a specified size from the sequence of characters. Here's an overview of how this class functions and can be utilized:
Initialization: The class is initialized with two arguments: a string (
sequenceInput) and an integer (combSize).sequenceInputspecifies the characters to combine, andcombSizespecifies the size of each combination. The initialization process involves setting up an arraysequenceIndiceswhich keeps track of the current indices of the characters in the combination.Generating the Next Combination:
- Call the
nextmethod to retrieve the next combination. - Construct the current combination based on the indices and update the indices to prepare for the next call.
- Update the internal state to ensure that combinations can continue to be provided until all possibilities are exhausted.
- Call the
Checking for More Combinations: The
hasNextmethod checks whether there are more combinations to generate by examining themoreCombinationsflag.
Usage Example:
- Instantiate the
CombinationGeneratorwith a specific string and combination size. - Use a loop that continues as long as
hasNextreturns true, within whichnextis called to retrieve the next combination.
This approach provides an efficient way to iterate over combinations without calculating them all at once, thereby supporting large sequences or large combinations sizes effectively.
class ComboIterator:
def __init__(self, chars: str, comboLength: int):
self.total_chars = len(chars)
self.combo_size = comboLength
self.all_chars = chars
# Initialize the first combination indices
self.indices = list(range(comboLength))
self.can_continue = True
def next(self) -> str:
idxs = self.indices
chars_num, combo_num = self.total_chars, self.combo_size
combination = [self.all_chars[j] for j in idxs]
# Finding the first index that can be incremented
i = combo_num - 1
while i >= 0 and idxs[i] == chars_num - combo_num + i:
i -= 1
idxs[i] += 1
# Create the next combination by increasing subsequent indices
if i >= 0:
for j in range(i + 1, combo_num):
idxs[j] = idxs[i] + j - i
else:
self.can_continue = False
return ''.join(combination)
def hasNext(self) -> bool:
return self.can_continue
This solution introduces a class ComboIterator to generate combinations of a specific length from a given string. The implementation is done in Python and encompasses two main methods (next and hasNext) along with an initialization method (__init__).
Initialize the
ComboIteratorclass with a string (chars) and the desired combination length (comboLength). During initialization, the method calculates the length ofchars, sets the combination length, and initializes the indices for the first combination.The
nextmethod is responsible for returning the current combination as a string. It calculates the combination based on current indices, then updates these indices to prepare for the next combination. The index updating involves checking which elements can still be incremented without exceeding the allowed combinations. If adjustments to indices can be made, the method sets up for the next combination; otherwise, it marks that no more combinations can be generated.The
hasNextmethod simply checks and returns a boolean value (self.can_continue) indicating whether there are more combinations to be returned.
This iterator efficiently handles combinations, leveraging sequential index adjustments to generate each subsequent combination, avoiding the need for recalculating or storing all possible combinations in memory. Such an approach is particularly useful for handling large datasets where memory efficiency is crucial.