MEDIUM

3. Longest Substring Without Repeating Characters

Given a string s, find the length of the longest substring without duplicate characters.

Examples:

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.

Constraints:

  • 0 <= s.length <= 5 * 10⁓
  • s consists of English letters, digits, symbols and spaces.

šŸ“‹ Step 1: Problem Identification

What type of problem is this?

šŸ”¢ Math/Number Theory
🌳 Tree Traversal
šŸŽÆ Two Pointers/Sliding Window
šŸ“Š Dynamic Programming
Attempts: 0
šŸ’” Hint: Think about maintaining a "window" of characters. When you encounter a duplicate, you need to shrink the window from one side. This technique is commonly used for substring problems.
āœ… Explanation: This is a classic sliding window problem. We need to find the longest substring without repeating characters, which is perfectly suited for the two-pointers/sliding window technique where we expand and contract a window based on conditions.

🧩 Step 2: Logical Intuition

Arrange the pseudocode blocks in the correct order:

Available Blocks:

Return maxLength
Initialize left = 0, maxLength = 0
For each right pointer from 0 to n-1
While character at right exists in current window
Move left pointer forward
Update maxLength if current window is larger

Solution Order:

Attempts: 0
šŸ’” Hint: Start with initialization, then think about expanding the window with the right pointer. When you find a duplicate, you need to shrink the window by moving the left pointer. Don't forget to track the maximum length!
āœ… Explanation: The correct order implements a sliding window: initialize variables, expand window with right pointer, contract when duplicates found by moving left pointer, track maximum length, and return result.

⚔ Step 3: Algorithm Selection

Which approach is most suitable for this problem?

Brute Force - Check all substrings
Sliding Window with HashSet
Binary Search
Divide and Conquer
Attempts: 0
šŸ’” Hint: We need to efficiently track which characters we've seen and be able to quickly check for duplicates. What data structure is perfect for O(1) lookups?
āœ… Explanation: Sliding Window with HashSet is optimal for this problem. It maintains a window of unique characters and efficiently expands/contracts it, achieving O(n) time complexity compared to O(n²) brute force.

ā±ļø Step 4: Complexity Analysis

function lengthOfLongestSubstring(s) { let maxLen = 0; for (let i = 0; i < s.length; i++) { let seen = new Set(); for (let j = i; j < s.length; j++) { if (seen.has(s[j])) break; seen.add(s[j]); maxLen = Math.max(maxLen, j - i + 1); } } return maxLen; }

What's the time complexity of this code?

O(n) - Linear
O(n²) - Quadratic
O(n log n) - Linearithmic
O(n³) - Cubic
Attempts: 0
šŸ’” Hint: Count the nested loops! The outer loop runs n times, and for each iteration, the inner loop can run up to n times in the worst case.
āœ… Explanation: This brute force approach has O(n²) complexity because of the nested loops. For each starting position (outer loop), we check all possible endings (inner loop). The optimal sliding window solution would be O(n).

šŸŽÆ Step 5: Output Prediction

Given the input string "pwwkew", what will be the output?

4 (substring "pwke")
3 (substring "wke")
2 (substring "pw")
6 (entire string)
Attempts: 0
šŸ’” Hint: Remember that a substring must be contiguous! Trace through "pwwkew" and find the longest contiguous part without any repeating characters.
āœ… Explanation: For "pwwkew", the longest substring without repeating characters is "wke" with length 3. Note that "pwke" is a subsequence, not a substring (must be contiguous).

šŸ› Step 6: Edge Case Detection

function lengthOfLongestSubstring(s) { let left = 0, maxLen = 0; let charMap = new Map(); for (let right = 0; right < s.length; right++) { if (charMap.has(s[right])) { left = charMap.get(s[right]) + 1; } charMap.set(s[right], right); maxLen = Math.max(maxLen, right - left + 1); } return maxLen; }

This code has a bug! Enter a test case that will make it fail:

Attempts: 0
šŸ’” Hint: Think about what happens when the left pointer would move backward. Try a string where you see a character again, but it appeared before the current left position.
āœ… Explanation: Try "abba" or "tmmzuxt". The bug occurs when the left pointer moves backward. For "abba": when we see the second 'a', left jumps to position 1, but should stay at 2.

šŸ” Step 7: Code Debugging

Click on the line that contains the bug:

function lengthOfLongestSubstring(s) { let left = 0, maxLen = 0; let charMap = new Map(); for (let right = 0; right < s.length; right++) { if (charMap.has(s[right])) { left = charMap.get(s[right]) + 1; } charMap.set(s[right], right); maxLen = Math.max(maxLen, right - left + 1); } return maxLen; }
Attempts: 0
šŸ’” Hint: Look for the line where the left pointer is updated. The bug is that the left pointer can move backward, which breaks the sliding window property.
āœ… Explanation: The bug is in line "left = charMap.get(s[right]) + 1;" because it can move the left pointer backward. The fix is to use Math.max(left, charMap.get(s[right]) + 1).

āœļø Step 8: Code Completion

Fill in the blanks to fix the bug:

function lengthOfLongestSubstring(s) { let left = 0, maxLen = 0; let charMap = new Map(); for (let right = 0; right < s.length; right++) { if (charMap.has(s[right])) { left = (charMap.get(s[right]) + 1, ); } charMap.set(s[right], right); maxLen = Math.max(maxLen, right - left + 1); } return maxLen; }
Attempts: 0
šŸ’” Hint: You need to ensure the left pointer never moves backward. What function can help you choose the maximum between two values?
āœ… Explanation: The correct code is "left = Math.max(left, charMap.get(s[right]) + 1);" - this ensures the left pointer never moves backward, which maintains the sliding window property.