给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
方法一:滑动窗口
首先明确是求最长子串而不是最长子序列,子串是必须得连续的,这里稍微模拟一下,以“pwwkew”为例,
查看p是否已经在集合中,否,更新ans,
查看w是否已经在集合中,否,更新ans,
查看w是否已经在集合中,是,那么w重复了,所以需要更新左边界,从左开始移除元素,直到w不在集合中,再插入,然后更新ans…
public int lengthOfLongestSubstring(String s) {
int len = s.length();
int i = 0, j = 0; //窗口为[i, j),也就是初始化为空
int ans = 0;
Set<Character> set = new HashSet<>();
while (i<len && j<len){
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j));
ans = Math.max(ans, j-i+1);
j++;
}
else {
set.remove(s.charAt(i));
i++;
}
}
return ans;
}
方法二:优化的滑动窗口
使用一个Map<Character, Integer>
,key存字符,value存key在字符串中的下标+1,这样当扩展窗口碰到key已经存在时,就可以快速更新左边界为max(i, map.get(s.charAt(j)))
,虽然这里可能map内存的元素多余当前窗口的大小,但并不不会影响。
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int j = 0, i = 0;j<s.length();j++){
if (map.containsKey(s.charAt(j))){
i = Math.max(i, map.get(s.charAt(j)));
}
ans = Math.max(j-i+1, ans);
map.put(s.charAt(j), j+1);
}
return ans;
}