LeetCode3(滑动窗口)

907 阅读2分钟
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 
 示例 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) 之间的字符,然后判断第 个字符 是否在set中,如果不在存入set,j++,即现在 set 保存了 s[i, j+1) 之间的字符;如果在,则 set 删除第 个字符,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 之间的任意位置 到 之间的子串都是有重复的,由此可以直接将 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;
    }
}