90%, 90% 2 solutions: Bactracking Generator and Iterator o(m+n) space and o(m) time to init
Bactracking generator:\npython\ndef _iter_string_combinations(characters: str, combination_length: int):\n    if combination_length == 0:\n        yield \'\'\n 
Bactracking generator:\n``` python\ndef _iter_string_combinations(characters: str, combination_length: int):\n    if combination_length == 0:\n        yield \'\'\n    elif combination_length == 1:\n        yield from characters\n    elif len(characters) == combination_length:\n        yield characters\n    else:\n        for char_index, char in enumerate(characters[:len(characters)-combination_length+1]):\n            for next_result in _iter_string_combinations(characters[char_index+1:], combination_length-1):\n                yield char + next_result\n\n\nclass CombinationIterator:\n    def __init__(self, characters: str, combinationLength: int):\n        self._iter = _iter_string_combinations(characters, combinationLength)\n        self._next = self._get_next()\n        \n    def _get_next(self) -> None:\n        try:\n            return next(self._iter)\n        except StopIteration:\n            return None\n    \n    def next(self) -> str:\n        _next = self._next\n        self._next = self._get_next()\n        return _next\n        \n    def hasNext(self) -> bool:\n        return not self._next is None\n```\nIterator:\n```\nclass CombinationIterator:\n    def __init__(self, characters: str, combinationLength: int):\n        self._characters = characters\n        self._combination_length = combinationLength\n        self._indices = list(range(self._combination_length))\n        self._exausted = False\n        \n    def _shift_indices(self) -> None:\n        for i in reversed(range(self._combination_length)):\n            if self._indices[i] != i + len(self._characters) - self._combination_length:\n                last_not_max_index = i\n                break\n        else:\n            self._exausted = True\n            return\n        self._indices[last_not_max_index] += 1\n        for j in range(last_not_max_index+1, self._combination_length):\n            self._indices[j] = self._indices[j-1] + 1\n    \n    def next(self) -> str:\n        result = \'\'.join(self._characters[i] for i in self._indices)\n        self._shift_indices()\n        return result\n    \n    def hasNext(self) -> bool:\n        return not self._exausted\n```
['Iterator', 'Python', 'Python3']