Key here is to use a dictionary to store the indexes. Use the characters as keys. For every pass, check the dictionary to see if the character is unique. If it is, increase the window. If it is not, then check if that was the longest substring, then set the left pointer to the index of the duplicate character and the right pointer to the next character and reset the dictionary.
def longest_substring_without_repeating_characters(s: str) → int:
longest_substr = left = right = 0
unique_chars = {}
while right < len(s):
if s[right] in unique_chars:
# sequence broken, char is no longer unique. Check if that sequence was the longest seen so far
longest_substr = max(longest_substr, len(unique_chars))
# move window forward. Left should move to the value after the duplicate character
left = unique_chars[s[right]] + 1
right = left
# cleanup. Reset unique character set
unique_chars.clear()
else:
# character is unique. Add to set
unique_chars[s[right]] = right
# Check if the sequence is the longest seen so far
longest_substr = max(longest_substr, len(unique_chars))
# keep finding more unique characters by expanding window
right += 1
return longest_substr