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

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

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

链接 https://leetcode.cn/problems/longest-substring-without-repeating-characters/

个人思路
  1. 暴力解法
    (1)当s长度小于等于1时,不可能存在重复字符,此时直接返回s长度
    (2)当s长度大于1时,从数据挖掘求解关联规则得到启发,频繁项集的子项集必然也是频繁的,而这里则是不含有重复字符的子串的子串必然也是不含重复字符的,因此我们可以循环得到长度为2的所有的子字符串,把子字符串长度逐渐递增,当得到的子字符串长度不再增加时,得到的就是最长子字符串的长度
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s) <= 1:
            return(len(s))
        result = 1
        # 从长度为2的子串开始
        for i in range(2,len(s)+1):
            for j in range(len(s)):
                # 长度超出则退出当前循环
                if i+j > len(s):
                    break
                else:
                    if len(set(s[j:j+i])) == i:
                        temp = len(set(s[j:j+i]))
                        result = temp if temp > result else result
           # 得到的长度不再增加,说明后续继续增加长度进行切片也毫无意义
           # 此时可以退出循环返回结果
            if result < i:
                break
        return result

但耗时非常长
LeetCode 3. 无重复字符的最长子串_第1张图片
2. 可以优化暴力解法中的算法,因为频繁多项集必然从其频繁子项集的组合中产生,所以这里我们可以记录每次可以得到不含重复项的较短子字符串的开始位置,下次从该位置进行更大的切片进行判断

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s) <= 1:
            return(len(s))
        else:
            maxLen = 1
            mark = [i for i in range(len(s))]
            # 记录下次开始切片位置
            mark = set(mark)
            for i in range(2,len(s)+1):
            # 不符合条件的位置
                deleteMark = []
                for j in mark:
                    if j+i > len(s):
                    # 超出界限的位置直接移除
                        deleteMark.append(j)
                    else:
                        if len(set(s[j:j+i])) == i:
                            maxLen = i
                        else:
                            deleteMark.append(j)
                # 当长度不再增加时退出返回结果
                if maxLen < i:
                    return maxLen
                mark = mark - set(deleteMark)
        return maxLen

从结果看,确实得到了优化,但仍然耗时较长
LeetCode 3. 无重复字符的最长子串_第2张图片

其他思路
  1. 官方思路:滑动窗口
    (1)假设我们选择字符串中的第 k 个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 r​ 。那么当我们选择第 k+1个字符作为起始位置时,首先从 k+1到 r的字符显然是不重复的,并且由于少了原本的第 kk 个字符,我们可以尝试继续增大 r ,直到右侧出现了重复字符为止。这样一来,我们就可以使用「滑动窗口」来解决这个问题了。
    (2)我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的 r ;
    (3)在每一步的操作中,我们会将左指针向右移动一格,表示 我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着 以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;在枚举结束后,我们找到的最长的子串的长度即为答案。
    (4)判断重复字符:在上面的流程中,我们还需要使用一种数据结构来判断 是否有重复的字符,常用的数据结构为哈希集合(即 C++ 中的 std::unordered_set,Java 中的 HashSet,Python 中的 set, JavaScript 中的 Set)。在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 哈希集合,记录每个字符是否出现过
        occ = set()
        n = len(s)
        # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        rk, ans = -1, 0
        for i in range(n):
            if i != 0:
                # 左指针向右移动一格,移除一个字符
                occ.remove(s[i - 1])
            while rk + 1 < n and s[rk + 1] not in occ:
                # 不断地移动右指针
                occ.add(s[rk + 1])
                rk += 1
            # 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1)
        return ans

LeetCode 3. 无重复字符的最长子串_第3张图片

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetc-2/
来源:力扣(LeetCode)

  1. 其他可参考
    https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-powcai/

你可能感兴趣的:(指针使用,leetcode,算法,职场和发展)