一、题目描述
1.1 题目
-
无重复字符的最长子串
-
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
-
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
- 示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
- 示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
1.2 知识点
- 哈希表
- 双指针
- 滑动窗口
1.3 题目链接
二、解题思路
2.1 自研思路
这道题的解题思路比较明确,我是通过哈希表来记录每个字符最后出现的下标,当一个字符重复出现时,会及时更新该字符最后出现的下标,并更新最长子串的起始下标,该其实下标的作用其实就相当于滑动窗口算法中的窗口左边框,主要是为了防止出现对于 abba 这种形式字符串可能错误判断的情况。
2.2 示例思路
官方提供的思想思路(窗口滑动的优化),其实跟我的算法是大致相同的,它对于普通的使用 HashSet 进行判重的逻辑进行优化,即当找到重复的字符时,可以立即跳过该窗口。也就是说,如果 s[j] 在 [i, j) 范围内有与 j′ 重复的字符,不需要逐渐增加 i ,而是可以直接跳过 [i,j′] 范围内的所有元素,并将 i 变为 j′+1(具体解题思路可见 LeetCode 官网,这里不再赘述)。
三、实现代码
3.1 自研实现
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> hashMap = new HashMap<>();
int cur = 0, len = 0, start = 0;
for(int i = 0; i < s.length(); i++){
if (hashMap.containsKey(s.charAt(i)) && hashMap.get(s.charAt(i)) >= start){
cur = i-hashMap.get(s.charAt(i));
start = i-cur+1;
} else {
cur += 1;
len = Math.max(len, cur);
}
hashMap.put(s.charAt(i), i);
}
return len;
}
}
3.2 示例代码
// 优化后的滑动窗口解法
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;
}
}