Iterator for Combination — Solution in Python, Java & C++
Iterator for Combination solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A medium problem.
Iterator for Combination is a medium coding problem on strings and backtracking, asked in Google interviews.
- Difficulty: Medium
- Topics: Strings, Backtracking
- Asked at: Google
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
Write the class CombinationIterator, which hands out the combinations of a set of letters one at a time, in lexicographical order:
CombinationIterator(String characters, int combinationLength)receives a string of sorted, distinct lowercase letters and the length of the combinations to produce.String next()returns the next combination ofcombinationLengthletters (each combination keeps the letters in their original order).boolean hasNext()returnstrueif a call tonextwould still produce a combination.
The judge calls the methods in the order listed in the first input line, with the arguments in the second; the expected output holds each call's result, null for the constructor and for every method that returns nothing.
Example 1
Input: ["CombinationIterator","next","hasNext","next","hasNext","next","hasNext"]
[["xyz",2],[],[],[],[],[],[]]
Output: [null,"xy",true,"xz",true,"yz",false]
Explanation: The combinations of two letters from xyz, in order, are xy, xz, yz. After the third one, hasNext is false.
Example 2
Input: ["CombinationIterator","hasNext","next","next","next","next"]
[["bdfg",3],[],[],[],[],[]]
Output: [null,true,"bdf","bdg","bfg","dfg"]
Explanation: Choosing three of b, d, f, g gives bdf, bdg, bfg, dfg — four combinations.
Constraints
1 <= combinationLength <= characters.length <= 15characters consists of distinct lowercase English letters, sorted ascendingAt most 10^4 calls are made to next and hasNextEvery call to next is valid
Hints for Iterator for Combination
Hint 1
Since the letters are sorted, a combination is just a set of increasing positions
i0 < i1 < … < i(k-1), and lexicographic order on the strings is lexicographic order on the positions.Hint 2
Start from positions
0, 1, …, k-1. To get the next combination, find the rightmost position that can still move right.Hint 3
Position
jcan move whileidx[j] < n - k + j(there must be room for the positions after it). Increase it by one and set every later position to the one before it plus one. If no position can move, the iteration is over.
How to solve Iterator for Combination
Generate combinations lazily from an array of k increasing positions, advancing it the way an odometer advances — rightmost movable digit first.
Approach
- Store the letters,
k, the positionsidx = [0, 1, …, k-1]and a flaghas = true. next(): build the answer from the letters atidx, then advance: find the largestjwithidx[j] < n - k + j.- If there is none, this was the last combination: set
has = false. Otherwise doidx[j]++andidx[t] = idx[t-1] + 1for everyt > j. hasNext(): returnhas.
Why it works
The positions after j are all at their maximum (n - k + t), so the current combination is the largest one with this prefix idx[0..j]. The smallest combination greater than it must change position j, by the least amount — one step — and then take the smallest possible tail, the consecutive positions after it. Because the letters are sorted and distinct, the order of position arrays is exactly the order of the strings.
Complexity
- Time —
O(k) per call - Space —
O(k)
Pitfalls
- Precomputing every combination works for n ≤ 15 (at most 6,435) but wastes memory and time when few are asked for.
- The bound for position
jisn - k + j, notn - 1: later positions need room after it. - hasNext must be decided after advancing; computing it from the combination about to be returned is off by one.
Reference solution
Python
class CombinationIterator:
def __init__(self, characters: str, combinationLength: int):
self.chars = characters
self.k = combinationLength
self.idx = list(range(combinationLength))
self.has = True
def next(self) -> str:
chars, k, idx = self.chars, self.k, self.idx
s = "".join(chars[i] for i in idx)
j = k - 1
while j >= 0 and idx[j] == len(chars) - k + j: # already at its rightmost place
j -= 1
if j < 0:
self.has = False
else:
idx[j] += 1
for t in range(j + 1, k):
idx[t] = idx[t - 1] + 1
return s
def hasNext(self) -> bool:
return self.hasJava
class CombinationIterator {
private String chars;
private int k;
private int[] idx;
private boolean has = true;
public CombinationIterator(String characters, int combinationLength) {
chars = characters;
k = combinationLength;
idx = new int[k];
for (int i = 0; i < k; i++) idx[i] = i;
}
public String next() {
StringBuilder sb = new StringBuilder();
for (int i : idx) sb.append(chars.charAt(i));
int n = chars.length(), j = k - 1;
while (j >= 0 && idx[j] == n - k + j) j--; // already at its rightmost place
if (j < 0) has = false;
else {
idx[j]++;
for (int t = j + 1; t < k; t++) idx[t] = idx[t - 1] + 1;
}
return sb.toString();
}
public boolean hasNext() {
return has;
}
}C++
class CombinationIterator {
string chars;
int k;
vector<int> idx;
bool has = true;
public:
CombinationIterator(string characters, int combinationLength) : chars(characters), k(combinationLength), idx(combinationLength) {
for (int i = 0; i < k; i++) idx[i] = i;
}
string next() {
string s;
for (int i : idx) s += chars[i];
int n = chars.size(), j = k - 1;
while (j >= 0 && idx[j] == n - k + j) j--; // already at its rightmost place
if (j < 0) has = false;
else {
idx[j]++;
for (int t = j + 1; t < k; t++) idx[t] = idx[t - 1] + 1;
}
return s;
}
bool hasNext() {
return has;
}
};JavaScript
var CombinationIterator = function(characters, combinationLength) {
this.chars = characters;
this.k = combinationLength;
this.idx = [];
for (var i = 0; i < combinationLength; i++) this.idx.push(i);
this.has = true;
};
CombinationIterator.prototype.next = function() {
var idx = this.idx, k = this.k, n = this.chars.length, s = "";
for (var i = 0; i < k; i++) s += this.chars[idx[i]];
var j = k - 1;
while (j >= 0 && idx[j] === n - k + j) j--; // already at its rightmost place
if (j < 0) this.has = false;
else {
idx[j]++;
for (var t = j + 1; t < k; t++) idx[t] = idx[t - 1] + 1;
}
return s;
};
CombinationIterator.prototype.hasNext = function() {
return this.has;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 515 strings problems · the whole catalogue
Learn the technique: Strings · Backtracking