Longest Substring Without Repeating Characters - JavaScript Solution with O(n) Time Complexity

Longest Substring Without Repeating Characters - JavaScript Solution with O(n) Time Complexity

Problem Definition

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

Input: A single string s.

Output: An integer representing the length of the longest substring without repeating characters.

Constraints:

Example:

Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.

Understanding the Problem

The core challenge of this problem is to find the longest substring in a given string that does not contain any repeating characters. This problem is significant in various applications such as data validation, parsing, and text processing. A common pitfall is to overlook the need to update the starting point of the substring when a repeating character is found.

Approach

To solve this problem, we can use a sliding window technique. The idea is to use two pointers to represent the current window of characters being considered. We expand the window by moving the right pointer and check if the character is already in the current window. If it is, we move the left pointer to the right of the first occurrence of the repeating character. This ensures that the window always contains unique characters.

Let's discuss a naive solution first:

Naive Solution

The naive approach is to generate all possible substrings and check each one for unique characters. This approach is not optimal because it has a time complexity of O(n3), where n is the length of the string. This is due to the nested loops required to generate substrings and the additional loop to check for uniqueness.

Optimized Solution

The optimized solution uses the sliding window technique with a hash map to store the characters and their indices. This allows us to efficiently check for repeating characters and update the window accordingly. The time complexity of this approach is O(n), where n is the length of the string, because each character is processed at most twice.

Algorithm

Here is a step-by-step breakdown of the optimized algorithm:

  1. Initialize a hash map to store the characters and their indices.
  2. Initialize two pointers, left and right, both set to 0.
  3. Initialize a variable maxLength to store the length of the longest substring found.
  4. Iterate through the string using the right pointer.
  5. For each character, check if it is already in the hash map and its index is within the current window.
  6. If it is, update the left pointer to the right of the first occurrence of the repeating character.
  7. Update the hash map with the current character and its index.
  8. Update maxLength with the length of the current window if it is larger than the previous maximum.
  9. Return maxLength after the loop ends.

Code Implementation

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    // Hash map to store the characters and their indices
    let charIndexMap = new Map();
    // Initialize pointers and maxLength
    let left = 0, right = 0, maxLength = 0;

    // Iterate through the string
    while (right < s.length) {
        // Get the current character
        let currentChar = s[right];

        // Check if the character is in the map and within the current window
        if (charIndexMap.has(currentChar) && charIndexMap.get(currentChar) >= left) {
            // Update the left pointer
            left = charIndexMap.get(currentChar) + 1;
        }

        // Update the map with the current character and its index
        charIndexMap.set(currentChar, right);

        // Update maxLength if the current window is larger
        maxLength = Math.max(maxLength, right - left + 1);

        // Move the right pointer
        right++;
    }

    // Return the length of the longest substring
    return maxLength;
};

Complexity Analysis

The time complexity of the optimized solution is O(n), where n is the length of the string. This is because each character is processed at most twice, once by the right pointer and once by the left pointer. The space complexity is O(min(n, m)), where m is the size of the character set, because the hash map stores at most m characters.

Edge Cases

Here are some potential edge cases and how the algorithm handles them:

Examples:

Input: s = ""
Output: 0

Input: s = "abcdef"
Output: 6

Input: s = "aaaaaa"
Output: 1

Testing

To test the solution comprehensively, we should include a variety of test cases:

Example test cases:

console.log(lengthOfLongestSubstring("abcabcbb")); // Output: 3
console.log(lengthOfLongestSubstring("bbbbb")); // Output: 1
console.log(lengthOfLongestSubstring("pwwkew")); // Output: 3
console.log(lengthOfLongestSubstring("")); // Output: 0
console.log(lengthOfLongestSubstring("abcdef")); // Output: 6

Thinking and Problem-Solving Tips

When approaching such problems, it is important to:

Conclusion

In this blog post, we discussed how to solve the problem of finding the longest substring without repeating characters using JavaScript. We covered the problem definition, approach, algorithm, code implementation, complexity analysis, edge cases, and testing. Understanding and solving such problems is crucial for improving problem-solving skills and preparing for coding interviews. Keep practicing and exploring further to master these concepts.

Additional Resources