1286. Iterator for Combination
Description of Problem
Design the CombinationIterator class:
CombinationIterator(string characters, int combinationLength)Initializes the object with a stringcharactersof sorted distinct lowercase English letters and a numbercombinationLengthas arguments.next()Returns the next combination of lengthcombinationLengthin lexicographical order.hasNext()Returnstrueif and only if there exists a next combination.
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(); // return "ab"
itr.hasNext(); // return True
itr.next(); // return "ac"
itr.hasNext(); // return True
itr.next(); // return "bc"
itr.hasNext(); // return False
Constraints:
1 <= combinationLength <= characters.length <= 15- All the characters of
charactersare unique. - At most
10^4calls will be made tonextandhasNext. - It is guaranteed that all calls of the function
nextare valid.
Solution
Tags: Permutation and Combination
Explanation
We can reduce combination problems to permutation problems since choosing k elements from n elements can be treated as permutating list of 0 and 1 where the number of 1 equals to k and of 0 equals to n-k.
For example, choosing 3 elements from [1,2,3,4,5], we have [1,2,3] ([1,1,1,0,0]), [1,2,4] ([1,1,0,1,0]) and so on.
Therefore, we can reuse the solution from [46. Permutations]
Code (Rust)
struct CombinationIterator {
characters : Vec<char>,
perm_iter : PermutationIteratorReverse<bool>,
}
impl CombinationIterator {
fn new(characters: String, length: i32) -> Self {
let mut state = vec![false ; characters.len()];
for i in 0..length as usize {
state[i] = true;
}
CombinationIterator {
characters : characters.chars().collect(),
perm_iter : PermutationIteratorReverse::new(state),
}
}
fn next(&mut self) -> String {
use std::iter::zip;
zip(
&self.characters,
&self.perm_iter.next().unwrap()
).into_iter()
.filter(|(_,b)| {
**b
})
.map(|(c,_)| *c)
.collect()
}
fn has_next(&self) -> bool {
self.perm_iter.has_next()
}
}
Appendix A1 - PermutationIteratorReverse
pub struct PermutationIteratorReverse<T : Ord + Clone>(Option<Vec<T>>);
impl<T : Ord + Clone> PermutationIteratorReverse<T> {
pub fn new(mut elements : Vec<T>) -> Self {
elements.sort();
elements.reverse();
PermutationIteratorReverse(Some(elements))
}
pub fn has_next(&self) -> bool {
self.0.is_some()
}
fn next_permutation(&mut self) -> Option<Vec<T>> {
let res = self.0.clone();
self.0 = self.0.take().and_then(|mut v|{
let i = Self::find_pivot(&v)?;
let j = Self::find_target(&mut v, i);
v.swap(i,j);
let _ = &mut v[i+1..].reverse();
Some(v)
});
return res;
}
fn find_pivot(elements : &[T]) -> Option<usize> {
if elements.len() < 2 {
return None;
}
let (mut i, mut j) = (elements.len() - 2, elements.len() - 1);
while /* i >= 0 && */ j >= 1 {
if elements[i] > elements[j] {
return Some(i);
}
i = i.checked_sub(1).unwrap_or(0);
j = j.checked_sub(1).unwrap_or(0);
}
return None;
}
fn find_target(elements : &mut [T], pivot : usize) -> usize {
let pivot_value = &elements[pivot];
let mut target_index = pivot+1;
let mut target_value = &elements[target_index];
for j in pivot+2..elements.len() {
let v = &elements[j];
if v < pivot_value && v >= target_value {
target_value = v;
target_index = j;
}
}
return target_index;
}
}
impl<T : Ord + Clone> Iterator for PermutationIteratorReverse<T> {
type Item = Vec<T>;
fn next(&mut self) -> Option<Self::Item> {
self.next_permutation()
}
}