Longest Substring Without Repeating Characters
When we given a String (s), it should find the length of the longest substring without repeating the characters.
Example 1:ย
String s = “abcdabcab”
Output: 4
Explanation: The answer is “abcd”, with the length of 4.
โ Optimal Java Solution (Time: O(n), Space: O(min(n, m)))
Here We can use the sliding window technique with a HashMap to track the index of characters.
import java.util.HashMap;
import java.util.Map;
public class LongestSubstring {
public int lengthOfLongestSubstring(String s) {
Map map = new HashMap<>();
int maxLen = 0, left = 0;
for(int right =0 ; right< s.length(); right++) {
char current = s.charAt(right);
if(map.containsKey(current)) {
left = Math.max(left, map.get(current)+1);
}
map.put(current, right);
maxLen = Math.max(maxLen, right-left+1);
}
return maxLen;
}
public static void main(String[] args) {
System.out.println(new LongestSubstring().lengthOfLongestSubstring("abcdabcab"));
}
}
โ Explanation:
๐งช Example Input:
String s = “abcabcbb”;
โ
Step 1: Initialize Variables
HashMap<Character, Integer> map = new HashMap<>();
int maxLen = 0;
int left = 0;
map
: Stores the most recent index of each character.maxLen
: Keeps track of the longest substring length found.left
: Left boundary of the current window (substring).
โ Step 2: Iterate Over the String (Right Pointer)
We’ll move a pointer right
from 0 to s.length() - 1
.
ย
๐ Iteration Breakdown:
โค right = 0
, char = 'a'
'a'
is not inmap
.Store
'a' โ 0
in map.Window length = right - left + 1 = 1
maxLen = 1
โค right = 1
, char = 'b'
'b'
is not inmap
.Store
'b' โ 1
in map.Window length = 2
maxLen = 2
map = { a: 0, b: 1 }, left = 0, maxLen = 2.
โค right = 2
, char = 'c'
'c'
is not inmap
.Store
'c' โ 2
in map.Window length = 3
maxLen = 3
map = { a: 0, b: 1, c: 2 }, left = 0, maxLen = 3
โค right = 3
, char = 'a'
'a'
is already inmap
at index0
Move
left
tomax(left, map.get('a') + 1) = max(0, 1) = 1
Update
'a' โ 3
in map.Window length = 3
(3 - 1 + 1
)maxLen = 3
(no change)
map = { a: 3, b: 1, c: 2 }, left = 1, maxLen = 3
โค right = 4
, char = 'b'
'b'
is at index1
Move
left = max(1, 2) = 2
Update
'b' โ 4
Window length = 3
maxLen = 3
map = { a: 3, b: 4, c: 2 }, left = 2, maxLen = 3
โค right = 5
, char = 'c'
'c'
is at index2
Move
left = max(2, 3) = 3
Update
'c' โ 5
Window length = 3
maxLen = 3
map = { a: 3, b: 4, c: 5 }, left = 3, maxLen = 3
โค right = 6
, char = 'b'
'b'
at index4
Move
left = max(3, 5) = 5
Update
'b' โ 6
Window length = 2
maxLen = 3
map = { a: 3, b: 6, c: 5 }, left = 5, maxLen = 3
โค right = 7
, char = 'b'
'b'
at index6
Move
left = max(5, 7) = 7
Update
'b' โ 7
Window length = 1
maxLen = 3
map = { a: 3, b: 7, c: 5 }, left = 7, maxLen = 3
โ Final Answer:ย
return maxLen = 3
โ
Why do we calculate right - left + 1
?
ย
right - left
: distance between two pointers (exclusive of the first one)right - left + 1
: distance including bothleft
andright
indexes โ the actual substring length
ย
JAVA8 Code
import java.util.*;
import java.util.stream.IntStream;
public class Java8Style {
public int lengthOfLongestSubstring(String s) {
Map map = new HashMap<>();
final int[] left = {0};
return IntStream.range(0, s.length())
.map(right -> {
char c = s.charAt(right);
if (map.containsKey(c)) {
left[0] = Math.max(left[0], map.get(c) + 1);
}
map.put(c, right);
return right - left[0] + 1;
})
.max()
.orElse(0);
}
}