Skip to main content

Longest Substring Without Repeating Characters

How to notice that the problem asks for a window that expands and contracts, not a full restart at every repetition.

Given a string s, return the length of the longest substring without repeating characters.

Example:

Input:  s = "abcabcbb"
Output: 3

The longest substring without repetition is "abc", with length 3.

What to notice before coding

Common mistakes

Step 1 — Understand what invalidates the substring

As long as all characters are unique, the current window stays valid.

The problem starts when a repetition appears.

The question is not "which substring should I test now?".

The real question is:

how do I keep a valid window while walking through the string?

Step 2 — Write the correct baseline

A good baseline is to fix a start and extend the end until a repetition appears:

export function lengthOfLongestSubstring(s: string): number {
  let best = 0

  for (let start = 0; start < s.length; start += 1) {
    const seen = new Set<string>()

    for (let end = start; end < s.length; end += 1) {
      const char = s[end]

      if (seen.has(char)) {
        break
      }

      seen.add(char)
      best = Math.max(best, end - start + 1)
    }
  }

  return best
}

It works. But it restarts too much work for every new starting point.

Step 3 — Ask what really needs to leave the window

If right finds a repeated character, do you need to clear everything?

No.

You only need to remove from the left until that character is no longer duplicated.

That is the heart of sliding window.

Step 4 — Expand and contract under one rule

Use:

  • right to expand
  • left to shrink when the rule breaks
  • a Hash Set to represent the characters inside the current window

The window only moves forward. It never needs to go backward.

The interactive editor needs JavaScript. You can still read the prompt and copy the starter code below.

Starter code

export function lengthOfLongestSubstring(s: string): number {
  // your solution here
  return 0
}
solution.ts
Stuck? Hints available.

Haven't solved it yet?

Viewing the solution now may reduce learning. It's worth trying a bit more.

Open the reference solution

Without JavaScript, the reference solution is shown inline instead of in a dialog.

Solution

Final complexity

Time: O(n)

Space: O(min(n, charset))

Solution 1 — Test every start

Good baseline for seeing the rule clearly.

export function lengthOfLongestSubstring(s: string): number {
  let best = 0

  for (let start = 0; start < s.length; start += 1) {
    const seen = new Set<string>()

    for (let end = start; end < s.length; end += 1) {
      const char = s[end]

      if (seen.has(char)) {
        break
      }

      seen.add(char)
      best = Math.max(best, end - start + 1)
    }
  }

  return best
}

Solution 2 — Sliding window with Hash Set in O(n)

Now the window adjusts without starting over.

export function lengthOfLongestSubstring(s: string): number {
  const window = new Set<string>()
  let left = 0
  let best = 0

  for (let right = 0; right < s.length; right += 1) {
    const char = s[right]

    while (window.has(char)) {
      window.delete(s[left])
      left += 1
    }

    window.add(char)
    best = Math.max(best, right - left + 1)
  }

  return best
}

Each character enters and leaves the window at most once.

What to say in the interview

A strong short explanation would be:

The baseline tests each starting point and costs O(n²). The optimized version maintains a window with no repeated characters. I expand with right, and when a duplicate appears I shrink with left only as much as needed until the window becomes valid again. That reduces the problem to O(n).

That shows that you:

  • understood the rule that invalidates the window
  • explained sliding window as controlled movement
  • avoided treating the optimization like a blind template

When this pattern shows up again

The same pattern comes back when you need to:

  • keep a valid window
  • expand while the rule holds
  • shrink until a violation is fixed
  • count or maximize something inside a moving contiguous range

The point is not memorizing “sliding window = two pointers.”

The point is noticing when the problem is about a contiguous range with a local rule.

Next challenge 3Sum Previous challenge Climbing Stairs

Related challenges

Related articles