LeetCode之HOT100--003 无重复字符的最长子串

312 阅读3分钟

「这是我参与11月更文挑战的3天,活动详情查看:2021最后一次更文挑战」。

前言

一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第3题003无重复字符的最长子串。

题目

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

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

示例 2:

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

示例 3:

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

示例 4:

输入: s = ""
输出: 0

提示:

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

分析

本题的目标非常清晰,就是找出字符串中无重复字符的最长子串,并返回最长子串的长度。
我们最容易想到的方法就是暴力解法(双循环法),即依次遍历以下标为index开始的最大无重复字符的子串,但是该方法的缺点是复杂度高,达到了O(n2)。该方法的基本思路如下:

1. 设置俩个指针, 左指针, 右指针同时指向第一个元素;
2. 移动右指针,直到右指针所指向的值等于左指针时, 终止本轮循环。
3. 让左指针向右走一位, 重置右指针, 让右指针等于左指针。
4. 重复第2,3 步, 直到左指针没有可以指向的值。

上述方法的复杂度的增加主要体现在左指针每次变化后,右指针完全是重新计算出来的。通过分析,我们发现滑动窗口的方法可以有效降低复杂度,但是需要一个字典帮助我们记录每一个字符上一次出现的下标。该方法的基本思路如下:

1.创建一个空字典preIndexDic,记录出现的每一个字符上一次出现的位置,设置指针startIndex,endIndex,分别表示当前最大不重复子串的开始和结束下标,都指向0,设置maxLen,表示字符串中当前的最大不重复子串的长度
2.判断preIndexDic中endIndex对应字符上一次出现的位置preIndex,如果preIndex在开始下标startIndex之后,则startIndex应该修改为从preIndex之后开始
3.计算当前不重复子串的长度,更新最大不重复子串长度maxLen,更新endIndex对应字符上一次出现的下标为endIndex
4.endIndex右移一位,重复2 3 4步,直到endIndex没有可以指向的值

题解

class Solution {
    func lengthOfLongestSubstring(_ s: String) -> Int {
        //字典的形式记录出现的字符及上一次出现的下标,key:value = ch:index
        var preIndexDic = Dictionary<Character, Int>() 
        var maxLen = 0  //记录最大长度
        var startIndex = 0  //记录开始当前子串开始的下标
        for (endIndex, ch) in s.enumerated() { 
            //当前字符上一次出现的位置,没有则为-1
            let preIndex = preIndexDic[ch] ?? -1 
            //如果当前字符上一次出现的下标在开始之后,则当前不重复子串应该从preIndex之后开始
            if preIndex >= startIndex { 
                startIndex = preIndex + 1 
            } 
            preIndexDic[ch] = endIndex  //刷新当前字符上一次出现的下标
            let curLen = endIndex - startIndex + 1  //计算以当前字符为结束字符的最大长度
            maxLen = maxLen > curLen ? maxLen : curLen  //刷新最大子串长度
        } 
        return maxLen 
    } 
}