1. 问题描述:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
2. 思路分析:
① 一开始的时候没有啥思路,所以看了一下官方的题解,官方的题解是使用map与滑动窗口来解决的,其中map是来存储字符串中字符出现的位置,这样就可以判断当前在循环中遍历的字符是否与之前的字符重复了,所以使用两个变量l,r来记录当前不重复子串的长度,假如没有重复那么变量j往右进行移动,假如发现存在重复的字符那么需要从记录子串的左边位置的变量进行删除字符l,进行窗口的缩小,所以这也叫做滑动窗口指导删除当前的字符之后没有存在了重复的字符,在发现不存在重复字符的情况下更新子串的长度,这个方法还是比较好理解的
② 在官方的提供的第二种解法中,是对于第一种滑动窗口解法的优化,当发现重复的字符之后直接将l的位置置为上一次map中的这个重复字符对应的下一个位置,省略了其中不必要的窗口的遍历,这个想法其实很巧妙,提高了效率,也比较难理解但是思路是很值得我们学习的,在学习的时候可以借助简单的测试用例与debug进行理解
3. 代码如下:
官方滑动窗口代码:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
}
优化的滑动窗口:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
}