给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 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 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
暴力法时间复杂度达到O(n^3), 显然会超时。这题使用到滑动窗口方法。
滑动窗口
定义一个 HashSet 储存 s 在 [i, j) 之间的字符,然后判断第 j 个字符 是否在set中,如果不在存入set,j++,即现在 set 保存了 s[i, j+1) 之间的字符;如果在,则 set 删除第 i 个字符,i++,直到s末尾。这过程中要始终记录 ans 为 j-i 的最大值。
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int n = s.length();
int i = 0;
int j = 0;
int ans = 0;
while(i < n && j < n){
if(!set.contains(s.charAt(j))){
set.add(s.charAt(j));
j++;
ans = Math.max(ans, j - i);
}
else{
set.remove(s.charAt(i));
i++;
}
}
return ans;
}
}优化滑动窗口
我们可以推断出,当 s[j] 出现在前面的子串 [i, j) 中,假设出现在 k 位置上,那么 i~k 之间的任意位置 p 到 j 之间的子串都是有重复的,由此可以直接将 i 增加到 k+1。
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int ans = 0;
HashMap<Character, Integer> map = new HashMap<>();
for(int i = 0, j = 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;
}
}