leetcode-无重复字符的最长子串

231 阅读2分钟

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

第3天,继续挑战第3题。

题目

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

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

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

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

示例 4:
输入: s = ""
输出: 0

思路

一开始读题可能会有一个疑问,这跟这个字符串中有多少个不同的字符有什么区别?示例3就给出了答案,这里的子串,必须是连续的。
其实这题几乎当做是滑动窗口的例题。
可以遍历整个字符串,从每个位置开始找到当前字符串的最长子串,然后在比较长度找出最长的就好了,过程如下图所示:

滑动窗口.png

一个重要的优化点是,当从位置k开始的字符串,找到最长子串Ak...A(k+m-1),那么从位置k+1开始,到位置k+m-1必然是不重复的,这部分在上一步已经算过了。反映到代码上,用一个Set存储了当前子串的长度,计算下一个开始时,可以从Set中把Ak移除,然后从A(k+m)开始判断是否跟Set中的重复。
这整个过程就像是用一个框来把最长子串框出来,这个框就想一个窗口,从左到右移动了一遍,所以滑动窗口这个名称还是挺形象的。

注意

注意点在示例4里面已经给出了,注意数组未空的情况。 还有一个优化点是,剩余字符串的长度不足已经找到的最长子串的长度时,不需要继续了,比如上图中的6、7这2行,当前最长子串长度是3,还剩余未遍历长度是2,此时就可以不继续遍历了,因为剩余的即使全部不重复,也不可能更长了。

Java版本代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int length = s.length();
        if (length == 0) {
            return 0;
        }
        Set<Character> set = new HashSet<>();
        int result = 0;
        int end = -1;
        for (int startIndex = 0; startIndex < length; startIndex++) {
            // 剩余字符串的长度不足已经找到的最长子串的长度时,不需要继续了,因为剩余的即使全部不重复,也不可能更长了
            if (length - startIndex <= result) {
                break;
            }
            // 当前最大长度等于上一次字串的长度-1,上一个字串长度为 end - (startIndex - 1) + 1 
            int max = end - (startIndex - 1) + 1 - 1;
            // startIndex > 0的时候,需要把startIndex - 1处的字符移走
            if (startIndex > 0) {
                set.remove(s.charAt(startIndex - 1));
            }
            while (end < (length - 1) && !set.contains(s.charAt(end + 1))) {
                set.add(s.charAt(end + 1));
                max++;
                end++;
            }
            if (max > result) {
                result = max;
            }
        }
        return result;
    }
}