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.
题目:给定一个字符串s,找出s中没有重复字符的最长子字符串,返回这个最长子字符串的长度。注意这里是子字符串,即位置必须是连续的。
基本思路:用sliding window检查一个子字符串是否含有重复字符,如果没有重复字符则扩大窗口,否则缩小窗口。另外,题目要求的是最大长度,因此每找到一个长度为L的无重复子字符串,都要更新结果,即result = max(result, L)。
实现思路1:使用HashSet保存不重复的字符。假设[i, j - 1]没有重复字符,检查j所指字符是否与该区间的字符重复:如果不重复则将j所指字符存进HashSet,更新结果result = max(result, j - i + 1),将窗口扩大为[i, j],然后进入下一次循环检查j + 1所指的字符;如果重复则从HashSet中删除i所指字符,将缩小窗口为[i + 1, j - 1],然后进入下一次循环继续检查j所指的字符。
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s == null) throw new IllegalArgumentException("argument is null");
int length = s.length();
if (length == 0) return 0;
int i = 0, j = 0, result = 0;
Set<Character> set = new HashSet<>();
while (i < length && j < length) {
if (!set.contains(s.charAt(j))) {
set.add(s.charAt(j));
result = Math.max(result, j - i + 1);
j++;
}
else {
set.remove(s.charAt(i));
i++;
}
}
return result;
}
}
思路1的缺点:假设[i, j - 1]区间内含有j所指字符,思路1的做法是i向右移动一个位置,将窗口缩小至[i + 1, j - 1]。这里我们并不知道i是否与j所指字符相同,假设j'与j指向相同字符(i <= j' <= j - 1),如果j' = i则新窗口[i + 1, j - 1]恰好避开了重复字符,如果j' != i则[i + 1, j - 1]中仍然含有与j重复的字符,下一次循环的结果会将窗口缩小至[i + 2, j - 1],如果j' != i + 1则窗口继续缩小,直到缩小至[j' + 1, j - 1]才避开重复字符。所以,这个过程会在缩小窗口上(从i一个位置一个位置地寻找j')花费大量时间。因此,我们希望能一次性找到j',直接将窗口跟新为[j' + 1, j ],下一次循环从j + 1所指字符开始检查。
实现思路2:根据前面的分析,使用HashMap来保存字符与其对应的位置。不论有没有发现重复字符,指针j都会移动到下一个位置,因此指针j使用for循环。如果当前j所指字符不在HashMap中,更新result,将这个字符与其位置添加进HashMap,然后开始下一次循环。如果当前j所指字符在HashMap中,则从HashMap中获取其所在的位置并更新i,同时要在HashMap将这个字符的位置更新为j。这里需要注意,i要么不动、要么往右移动,不能往左移动。
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s == null) throw new IllegalArgumentException("argument is null");
int length = s.length();
if (length == 0) return 0;
Map<Character, Integer> map = new HashMap<>();
int result = 0;
for (int i = 0, j = 0; j < length; j++) {
if (map.containsKey(s.charAt(j)))
i = Math.max(i, map.get(s.charAt(j)) + 1);
result = Math.max(result, j - i + 1);
map.put(s.charAt(j), j);
}
return result;
}
}
参考资料:
https://leetcode.com/problems/longest-substring-without-repeating-characters/solution/