Use a sliding window. Expand the window by moving the right pointer. When you encounter a character already in the window, shrink from the left until it's removed.
A hash map tracks each character's most recent index. When the right pointer hits a character c that's already in the window, move the left pointer to just past c's previous position.
This optimization skips characters one by one. Instead of shrinking the window character by character, you jump directly to the right position.
With s = "abcabcbb", the window grows to "abc". Then you see 'a' again. Jump left past the old 'a' position. Window becomes "bca".