[LeetCode] 3. Longest Substring
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3.
Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.
</br>
Solution
Though the basic brute force way to lay out all the possible substrings to each determine whether it is without repeating characters may seem straightforward, this method is low time-efficiency and will result in TLE.
Hence we have to avoid using unnecessary loop and try to check the repeating characters while iterating all the possible substrings.
Therefore, we can consider establishing a slider to control which substring to choose. For the slider [ i, j ], if this substring does not contain repeating characters, then we increase the value of j as if we have broaden the slider in order to examine the next substring. On the contrary, if the substring does contain repeating substring, then we have to increase the value of i as if we slide the entire slider forward.
The code is shown as below.
Java
public class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLength = 0, i = 0, j = 0;
Set<Character> set = new HashSet<>();
while(i < s.length() && j < s.length()){
if (set.contains(s.charAt(j))){
set.clear();
i++;
j = i;
}
else{
set.add(s.charAt(j));
maxLength = Math.max(maxLength, j-i+1);
j++;
}
}
return maxLength;
}
}
One more thing, whether to use HashSet or HashMap can also makes a difference for their different time efficiency. Using HashMap in the code above can result in TLE, while switching to HashSet can avoid this problem.
</br>
Complexity Analysis
Time Complexity: O(n^2).
Space Complexity: O(n).
</br>
Further Development
Even though the code can be accepted, the efficiency can be further improved. We can actually optimize the code to check the substring in one pass.
When there is a duplicate in the current substring, instead of increasing i by one, we can actually skip all the characters before the next same duplicate letters.
For example, for string "dvdf"
, when encountering the second d, instead of increasing i by one, we can skip to the characters before the second "d".
Therefore, we can use the HashMap to record the position of the letters to determine the exact value i should increase.
The optimized code is shown below.
Java
public class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLength = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
for (int j = 0, i = 0; j < s.length(); j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
maxLength = Math.max(maxLength, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return maxLength;
}
}
</br>
Complexity Analysis
Time Complexity: O(n).
Space Complexity: O(n).
</br>
</br>