3. 无重复字符的最长子串

154 阅读1分钟

题目

3. 无重复字符的最长子串

难度中等7441收藏分享切换为英文接收动态反馈

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列, 不是子串。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

解法1:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length() == 0) {
            return 0;
        }
        int result = 1;
        int index = 1;
        HashSet<Character> set = new HashSet<>();
        int len = s.length();
        for (int i = 1; i < len; i++) {
            set.add(s.charAt(i));
            for (int j = i - 1; j >= 0; j--) {
                char c2 = s.charAt(j);
                if (set.contains(c2)) {
                    index = 1;
                    set.clear();
                    break;
                } else {
                    index++;
                    set.add(c2);
                }
                result = Math.max(result, index);
            }
            index = 1;
            set.clear();
        }
        return result;
    }
}

思路:

以为例:abcabcbb
一次遍历每个字符,作为一轮

第1轮:
1个字母a时,a前面无字符

第2轮: 2个字符时,依次从后往前遍历并存入map,如果当前遍历的字符在map中则停止
ba3轮:
cba4轮:
acb a
当再遍历到a时,map中已经存在了,则停止次轮循环,继续下一轮:

第5轮:
bac b...

max值每次更新

时间复杂度:O(N^2) n为数组长度

空间复杂度:O(N)

解法2 滑动窗口

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length() == 0) {
            return 0;
        }
        int maxLen = 1;
        HashSet<Character> set = new HashSet<>();
        set.add(s.charAt(0));
        int len = s.length();
        int right = 1;
        for (int i = 0; i < len; i++) {
            if (i != 0) {
                set.remove(s.charAt(i - 1));
            }
            while (right < len) {
                char cK = s.charAt(right);
                if (set.contains(cK)) {
                    break;
                }
                set.add(cK);
                maxLen = Math.max(maxLen, right - i + 1);
                right++;
            }
        }
        return maxLen;
    }
}

用左右指针分别记录,中间的是不重复的字符set。

每次移动右指针,一旦当前元素在set中,则将左指针右移一步。

时间复杂度:O(N) n为数组长度,左右指针分别会遍历这个那个字符串一次

空间复杂度:O(N)

解法3: 滑动窗口优化

右指针每次移动过程中,左指针的更新为:max(左指针,map中已存在的值+1)

举个例子:

abcabcbb

依次遍历abc再到a时
右指针为3
左指针为0map.a = 0
变化为左指针变为 max(0, map.a + 1) = 1


pwwkew

依次遍历pw再到w时
右指针为2
左指针为0map.w = 1
变化为左指针变为 max(0, map.a + 1) = 2
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int start = 0;
        int len = s.length();
        int res = 0;
        HashMap<Character, Integer> map = new HashMap<>();
        for (int end = 0; end < len; end++) {
            if (map.containsKey(s.charAt(end))) {
                start = Math.max(start, map.get(s.charAt(end)) + 1);
            }
            map.put(s.charAt(end), end);
            res = Math.max(res, end - start + 1);
        }
        return res;
    }
}

时间复杂度:O(N) n为数组长度

空间复杂度:O(N)