Loading problems...
TL;DR: Use a sliding window approach with a hash map to track the most recent index of each character, allowing the window's start pointer to jump over duplicates instantly.
The problem asks us to analyze a string s and determine the length of the longest contiguous substring that contains no duplicate characters. Unlike a subsequence, a substring must not have breaks. This is a classic optimization problem found in LeetCode 3: Longest Substring Without Repeating Characters.
The naive approach involves checking every possible substring to see if it contains unique characters. We can generate all substrings and iterate through them.
max_len = 0.i from 0 to n.j from i to .ns[i...j], check if all characters are unique (using a Set).max_len = max(max_len, j - i + 1).Pseudo-code:
textfunction bruteForce(s): n = length(s) max_len = 0 for i from 0 to n-1: for j from i to n-1: if allUnique(s, i, j): max_len = max(max_len, j - i + 1) return max_len
Complexity Analysis:
The time complexity is . The nested loops generate substrings, and checking uniqueness for each substring takes time.
Why it fails: With s.length up to , an or even an optimized solution will result in a Time Limit Exceeded (TLE) error. We need a linear time solution.
The brute force method performs redundant checks. If a substring s[i...j] is valid, but s[j+1] is a duplicate of a character at index k (where i <= k <= j), we do not need to check any substrings starting between i and k. Any substring starting in that range and ending at j+1 will still contain the duplicate pair.
The core insight is to maintain a window [left, right] that represents the current substring without repeating characters. As we extend right, if we encounter a character that already exists inside our current window, we must move the left pointer to exclude the previous occurrence of that character.
Instead of incrementing left one by one, we can use a Hash Map to store the last seen index of every character. This allows the left pointer to "jump" directly to index + 1 of the repeated character, instantly restoring the uniqueness property.
Visual Description:
Imagine the string as a timeline. The right pointer moves forward, adding characters to the window. When right lands on a character 'A' that was previously seen at index 5, and our current window starts at index 2, the window is now invalid. To fix this, we visualize the left boundary snapping from index 2 to index 6 (5 + 1). This effectively slices off the old 'A' and everything before it, making the new window valid and ready to continue expanding.

Consider Input: s = "abcabcbb"
left = 0, max_len = 0, map = {}.map = {'a': 0}. max_len = 1. Window: "a".map = {'a': 0, 'b': 1}. max_len = 2. Window: "ab".map = {'a': 0, 'b': 1, 'c': 2}. max_len = 3. Window: "abc".left to .map for 'a' to 3. map = {'a': 3, ...}.left to .map for 'b' to 4.left to .map for 'c' to 5.left to .map for 'b' to 6.left to .map for 'b' to 7.max_len = 3.The algorithm guarantees correctness because it exhaustively checks the longest possible substring ending at every index right.
For any ending position right, the optimal starting position left is the smallest index such that s[left...right] has no duplicates.
By maintaining left as max(left, prev_index_of_char + 1), we ensure left never moves backward and always skips past the first instance of a duplicate pair. Since we compute the length for every valid right and take the global maximum, the solution is optimal.
cppclass Solution { public: int lengthOfLongestSubstring(string s) { // Map to store the last seen index of each character. // Using vector<int> for ASCII optimization (size 256 covers extended ASCII). vector<int> charIndex(256, -1); int left = 0; int maxLen = 0; for (int right = 0; right < s.length(); right++) { char currentChar = s[right]; // If character is seen and is inside the current window if (charIndex[currentChar] != -1 && charIndex[currentChar] >= left) { // Move left pointer to the right of the previous occurrence left = charIndex[currentChar] + 1; } // Update the last seen index of the current character charIndex[currentChar] = right; // Update max length maxLen = max(maxLen, right - left + 1); } return maxLen; } };
javaclass Solution { public int lengthOfLongestSubstring(String s) { // Map to store character and its most recent index Map<Character, Integer> map = new HashMap<>(); int left = 0; int maxLen = 0; for (int right = 0; right < s.length(); right++) { char currentChar = s.charAt(right); // If duplicate found within the current window if (map.containsKey(currentChar)) { // Move left pointer, but ensure it never moves backward // Math.max is crucial here left = Math.max(left, map.get(currentChar) + 1); } // Update the map with the current index map.put(currentChar, right); // Calculate current window length and update max maxLen = Math.max(maxLen, right - left + 1); } return maxLen; } }
pythonclass Solution: def lengthOfLongestSubstring(self, s: str) -> int: # Dictionary to store the last seen index of characters char_index_map = {} left = 0 max_len = 0 for right in range(len(s)): current_char = s[right] # If the character is already in the map and is part of the current window if current_char in char_index_map and char_index_map[current_char] >= left: # Move the left pointer to the right of the previous occurrence left = char_index_map[current_char] + 1 # Update the last seen index char_index_map[current_char] = right # Update the maximum length found so far max_len = max(max_len, right - left + 1) return max_len
Time Complexity:
We iterate through the string once with the right pointer. The left pointer also moves only forward. Hash map operations (insert/find) are on average. Thus, the total time is linear relative to the string length.
Space Complexity: The space is determined by the size of the Hash Map. In the worst case, it stores every unique character in the string. represents the size of the character set (e.g., 128 for ASCII, 256 for Extended ASCII). Since the problem constraints allow symbols and spaces, the map size is bounded by the number of unique characters possible, which is constant for a fixed character set, or if the character set is effectively infinite.
The Sliding Window - Variable Size pattern is highly reusable. Similar logic applies to:
Mastering the interaction between the left pointer and the validity constraint is key to solving all these popular interview questions.
Why did I get the wrong answer when the duplicate was outside the window?
left = map[char] + 1 without checking if map[char] >= left.left).left backward re-introduces old characters, invalidating the "substring" logic and producing incorrect lengths.Why is my solution TLE (Time Limit Exceeded)?
max_lenif char in window_substringWhy is the length calculation off by one?
right - left instead of right - left + 1.