class StringIterator:
def __init__(self, compressedString: str):
self.res = []
self.ptr = 0
i = 0
while i < len(compressedString):
ch = compressedString[i]
i += 1
num = 0
while i < len(compressedString) and compressedString[i].isdigit():
num = num * 10 + int(compressedString[i])
i += 1
for j in range(num):
self.res.append(ch)
def next(self) -> str:
if not self.hasNext():
return ' '
char = self.res[self.ptr]
self.ptr += 1
return char
def hasNext(self) -> bool:
return self.ptr != len(self.res)We precompute the elements of the uncompressed string. Thus, the space required in this case is , where refers to the length of the uncompressed string.
The time required for precomputation is since we need to generate the uncompressed string of length .
Once the precomputation has been done, the time required for performing next() and hasNext() is for both.
This approach can be easily extended to include previous(), last() and find() operations. All these operations require the use of an index only and thus, take time. Operations like hasPrevious() can also be easily included.
Since once the precomputation has been done, next() requires time, this approach is useful if next() operation needs to be performed a large number of times. However, if hasNext() is performed most of the times, this approach isn't much advantageous since precomputation needs to be done anyhow.
A potential problem with this approach could arise if the length of the uncompressed string is very large. In such a case, the size of the complete uncompressed string could become so large that it can't fit in the memory limits, leading to memory overflow.
Where is the length of the uncompressed string.
class StringIterator:
def __init__(self, compressedString: str):
self.ptr = 0
import re
self.nums = list(map(int, re.findall(r'\d+', compressedString)))
self.chars = re.findall(r'[a-zA-Z]', compressedString)
def next(self) -> str:
if not self.hasNext():
return ' '
self.nums[self.ptr] -= 1
res = self.chars[self.ptr]
if self.nums[self.ptr] == 0:
self.ptr += 1
return res
def hasNext(self) -> bool:
return self.ptr != len(self.chars)The space required for storing the results of the precomputation is , where refers to the length of the compressed string. The and array contain a total of elements.
The precomputation step requires time. Thus, if hasNext() operation is performed most of the times, this precomputation turns out to be non-advantageous.
Once the precomputation has been done, hasNext() and next() require time.
This approach can be extended to include the previous() and hasPrevious() operations, but that would require making some simple modifications to the current implementation.
Where is the length of the compressed string.
class StringIterator:
def __init__(self, compressedString: str):
self.res = compressedString
self.ptr = 0
self.num = 0
self.ch = ' '
def next(self) -> str:
if not self.hasNext():
return ' '
if self.num == 0:
self.ch = self.res[self.ptr]
self.ptr += 1
while self.ptr < len(self.res) and self.res[self.ptr].isdigit():
self.num = self.num * 10 + int(self.res[self.ptr])
self.ptr += 1
self.num -= 1
return self.ch
def hasNext(self) -> bool:
return self.ptr != len(self.res) or self.num != 0Since no precomputation is done, constant space is required in this case.
The time required to perform next() operation is .
The time required for hasNext() operation is .
Since no precomputations are done, and hasNext() requires only time, this solution is advantageous if hasNext() operation is performed most of the times.
This approach can be extended to include previous() and hasPrevious() operations, but this will require the use of some additional variables.