Design and implement a data structure for an iterator of a compressed string. The given compressed string will be in the form of each letter followed by a positive decimal number representing the quantity of this letter.
Implement the StringIterator
class:
StringIterator(string compressedString)
Initializes the object with the given compressed string.char next()
Returns the next character if the compressed string still has characters, otherwise returns a space.boolean hasNext()
Returns true
if there are still some characters in the compressed string, otherwise returns false
.Example 1:
Input ["StringIterator", "next", "next", "next", "next", "next", "next", "hasNext", "next", "hasNext"] ["L1e2t1C1o1d1e1", [], [], [], [], [], [], [], [], []] Output [null, "L", "e", "e", "t", "C", "o", true, "d", true] Explanation StringIterator stringIterator = new StringIterator("L1e2t1C1o1d1e1"); stringIterator.next(); // return "L" stringIterator.next(); // return "e" stringIterator.next(); // return "e" stringIterator.next(); // return "t" stringIterator.next(); // return "C" stringIterator.next(); // return "o" stringIterator.hasNext(); // return True stringIterator.next(); // return "d" stringIterator.hasNext(); // return True
Constraints:
1 <= compressedString.length <= 1000
compressedString
consists of lower-case and upper-case English letters and digits.[1, 10^9]
.100
calls will be made to next
and hasNext
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for iterating through a compressed string means we first completely expand the string. Then, we simply move through the expanded version character by character. This is like taking the shortcut of writing everything out in full before starting to process it.
Here's how the algorithm would work step-by-step:
class CompressedStringIterator:
def __init__(self, compressed_string: str):
self.expanded_string = ""
index = 0
while index < len(compressed_string):
character = compressed_string[index]
index += 1
number_string = ""
while index < len(compressed_string) and compressed_string[index].isdigit():
number_string += compressed_string[index]
index += 1
# Construct the expanded string
quantity = int(number_string)
self.expanded_string += character * quantity
self.current_position = 0
self.string_length = len(self.expanded_string)
def next(self) -> str:
# Handle if we've reached the end of the string
if self.current_position >= self.string_length:
return ""
next_character = self.expanded_string[self.current_position]
self.current_position += 1
return next_character
def hasNext(self) -> bool:
# Check if there are any characters left
return self.current_position < self.string_length
The challenge involves iterating through a compressed string where characters are followed by repetition counts. The smart strategy is to keep track of the current character and its remaining count, updating both as we move through the string. We'll essentially unpack the string on demand, character by character.
Here's how the algorithm would work step-by-step:
class StringIterator:
def __init__(self, compressedString: str):
self.compressed_string = compressedString
self.string_length = len(compressedString)
self.current_index = 0
self.current_character = ''
self.character_remaining_count = 0
def next(self) -> str:
if not self.hasNext():
return ""
# If we've exhausted the current character, get the next one.
if self.character_remaining_count == 0:
self.extract_next_character_and_count()
self.character_remaining_count -= 1
return self.current_character
def hasNext(self) -> bool:
return self.current_index < self.string_length or self.character_remaining_count > 0
def extract_next_character_and_count(self) -> None:
self.current_character = self.compressed_string[self.current_index]
self.current_index += 1
# Extract the number which could be multiple digits.
count_string = ""
while self.current_index < self.string_length and self.compressed_string[self.current_index].isdigit():
count_string += self.compressed_string[self.current_index]
self.current_index += 1
# Convert the string to an integer for the count.
self.character_remaining_count = int(count_string)
# Your StringIterator object will be instantiated and called as such:
# string_iterator = StringIterator(compressedString)
# while string_iterator.hasNext():
# char = string_iterator.next()
# print(char)
Case | How to Handle |
---|---|
Null or empty compressed string | Return immediately as there is nothing to iterate through, possibly throwing an exception or returning null/empty iterator. |
String with only character and no count (e.g., 'A') | Treat it as a character with a count of 1, handling the missing count gracefully and iterating over it once. |
String with only a number (e.g., '10') | Throw an exception or return an error, as it's an invalid compressed string format with a missing character. |
Large count values that could lead to integer overflow | Use a data type that can hold larger numbers (e.g., long) or check for overflow during count parsing to prevent unexpected behavior. |
Consecutive digits in the compressed string (e.g., 'A12B3') | Properly parse the count by accumulating digits until a non-digit character is encountered, correctly identifying '12' as the count for 'A'. |
String with zero count (e.g., 'A0B5') | Skip the character associated with the zero count, effectively ignoring it in the expanded string, or throw an exception. |
The compressed string contains non-alphanumeric characters | Throw an exception or skip the invalid character, depending on desired behavior of the iterator implementation. |
Maximum length of the uncompressed string may lead to out of memory errors | Consider a generator/iterator pattern that computes each character on demand instead of pre-computing the entire uncompressed string in memory. |