Longest String Without Repeating Characters
Question statment
Given a string s, find the length of the longest substring without repeating characters.
Example:
Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Input: s = "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Input: s = "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
Solution
The idea is we need to keep counting until we find a duplication.
For example given this text:
abcabfdd
At second point a
where we find a duplicate. We need to start counting from where there is no duplication anymore (b
).
So:
v
abcabfdd
^
Because at b
, there is no duplication anymore. Similarly, when we find b
as duplicated, we need to count from where there is no duplication, hence c
:
v
abcabfdd
^
Similarly, when reaching second d
:
v
abcabfdd
^
We need to count from when there is no duplication anymore: the same d
v
abcabfdd
^
Implementation
As a result, we can see that we can keep track of a set
. When seeing a duplication, we can just keep poping from the set until it's not duplicated anymore, for example:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if len(s) <= 1: return len(s)
result = 0
slow, fast = 0, 1
visited = set(s[slow])
while (fast < len(s)):
while s[fast] in visited:
visited.remove(s[slow])
slow += 1
visited.add(s[fast])
result = max(result, fast - slow + 1)
fast += 1
return result
Time complexity: $O(n*2)$
- Some elements need to visited twice for the visited to remove.
- But however it should not goes over $O(n^2)$ because worst case
slow
can go up to the currentfast
and the algorithm keeps running.
Space complexity: $O(n)$ - We use a
set
to keep track of visited element
Optimisation
We can optimise our time complexity. It's obvious that we need to find a point at which there is no duplication anymore. As a result, we can start counting from the next character of the duplicated character's last appearance:
class App:
def lengthOfLongestSubstring(self, s: str) -> int:
letterToIndexMap = {}
left, right = 0, 0
maxLength = 0
while (right < len(s)):
currLetter = s[right]
if currLetter in letterToIndexMap and letterToIndexMap[currLetter] + 1 > left:
left = letterToIndexMap[currLetter] + 1
maxLength = max(maxLength, right - left + 1)
letterToIndexMap[currLetter] = right
right += 1
return maxLength
Time complexity: $O(n)$
Space complexity: $O(n)$
- We use a
dict
to keep track of visited element
[!note]
We need the condition thatletterToIndexMap[currLetter] + 1 > left
because without it theleft
pointer might move backwards. For our algorithm, we only allow it to move forward