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

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

题目:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

解题方法:

1.走尺法:

走尺法,start 是头,Length 是长度 只要Start+length 不超出字符长度就一直走下去,判断可以扩展的时候
length+1,不能扩招就Start+1
执行用时 :216 ms, 在所有 Python3 提交中击败了20.71%的用户
内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.88%的用户

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if s == "":
            return 0
        start = 0  #尺子开始
        length = 1 #尺子长度
        while ((start + length) < len(s)):
            if len(set(s[start:start + length+1])) == length+1:#判断尺子长度是否可以扩
                length += 1
            else:#不能扩   开始点就走一步
                start += 1
        return length
2.双列表法:

1.创建两个列表,L记录生成的子串,L_len记录子串的长度.
2.遍历S,如果不在L列表中,就添加进去.如果在列表中,就删除该值(含)之前的所有数,然后把这个新数添加到尾部,每一次判断之后都向L_len列表中添加L的长度(极大优化空间,困了).
3.用max函数获得L_len列表中的最大值.
执行用时 :80 ms, 在所有 Python3 提交中击败了66.28%的用户
内存消耗 :13.7 MB, 在所有 Python3 提交中击败了5.88%的用户

class Solution:
    def lengthOfLongestSubstring(self, s):
        if len(s) == 0:
            return 0
        elif len(s) == 1:
            return 1
        else:
            L = []
            L_len = []
            a = len(s)
            for x in range(0,a):
                if s[x] not in L:
                    L.append(s[x])
                else:
                    for i in range(0,L.index(s[x])+1):
                        L.remove(L[0])
                    L.append(s[x])
                L_len.append(len(L))
            return max(L_len)            
3.双指针法:

尾指针遍历字符串
当没有出现重复字符串时,头指针不变,
当出现重复字符时,头指针更新为重复字符的下一个位置。
头指针到头指针的差是当前识别的字符串长度。
执行用时 :68 ms, 在所有 Python3 提交中击败了88.27%的用户
内存消耗 :13.9 MB, 在所有 Python3 提交中击败了5.88%的用户

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        start=0
        rst=[0]
        for i in range(len(s)):
            if s[i] in s[start:i]:start=start+s[start:i].index(s[i])+1
            rst.append(i-start+1)
        return max(rst) 
4.优化滑动算法:

执行用时 :72 ms, 在所有 Python3 提交中击败了
81.61%的用户内存消耗 :
13.7 MB, 在所有 Python3 提交中击败了5.88%的用户

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        length = len(s)
        if(length <= 1):
            return length
        #创建字典,记录上次字母出现的位置
        temp = dict.fromkeys(list(set(s)),[0,0])
        #最大值
        Max = 1
        #子字符串起始位置
        start = 0
        for index, each in enumerate(s):
            #判断如果该字母已出现,并且在起始位置之后出现
            if(temp[each][0] != 0 and temp[each][1] >= start):
                #计算最大值
                Max = max(Max, index-start)
                #修改起始位置,为上一个出现位置的后一个
                start = temp[each][1] + 1  
            temp[each] = [1, index]
        return max(Max, length-start)

参考:



class Solution:

    # 方法一: 最容易想到的方法 (两层遍历; 需要开辟多个空间存储,占用较多内存)
    # 复杂度: O(n^2)
    def lengthOfLongestSubstring(self, s: str) -> int:
        all_length = [] # 存放"所有"的'不重复字符串'的长度
        lst = [] # 存储当前'无重复'的所有元素的列表.
        for i in s:
            # 如果重复了: 1.把当前的长度存入all_length 2.把lst中重复元素之前的元素都删掉
            if i in lst:
                all_length.append(len(lst))
                start_index = lst.index(i) + 1 # '重复元素'在lst中的位置
                lst = lst[start_index:]
            lst.append(i)
        all_length.append(len(lst))
        longest_length = max(all_length)
        return longest_length
    # 执行用时 : 64 ms , 在所有 Python3 提交中击败了 87.00% 的用户
    # 内存消耗 : 13.8 MB , 在所有 Python3 提交中击败了 5.02% 的用户


    # 方法二: 优化方法
        # 优化1: 使用数字longest_length: 一个int来替代lst (避免开辟新的存储空间)
        # 优化2: 使用双指针: 替代列表对象 (避免开辟多余列表空间: 仅有两个'指针变量')
        # todo: 如何处理最后一段无重复序列? (在循坏之外独立处理..可能不太好)
    # 复杂度: O(n^2)
    def lengthOfLongestSubstring(self, s: str) -> int:
        longest_length = 0 # 记录'最长序列'的长度
        left_index, right_index = 0, 0 # 初始化指针位置
        for right_index, e in enumerate(s):
            slice = s[left_index : right_index] # 能取到左边, 不能取到右边
            if e in slice: # 如果新取到的元素, 在指针范围内的'切片'中'能找到' [即: 有重复值]
                length = right_index - left_index # 计算该'无重复序列'的长度
                longest_length = max(length, longest_length) # 取大值更新longest_length
                # 左指针应该放在'前一个重复元素'的'右边' (这样才不会在下次计算的时候取到上一个重复元素)
                left_index = left_index + slice.index(e) + 1
        # 当整个序列s都没有重复的情况下, 需要单独考虑
        length = len(s) - left_index
        longest_length = max(length, longest_length)
        return longest_length
    # 执行用时 : 56 ms , 在所有 Python3 提交中击败了 96.41% 的用户
    # 内存消耗 : 13.7 MB , 在所有 Python3 提交中击败了 5.02% 的用户
    # todo: 内存消耗问题还是很大!!怎么解决??


    """
    方法三: 使用双指针 + 哈希表记录和获取已读索引
    # 复杂度: O(n)
        思路:
            1. 使用字典的哈希表记录索引: 取代了 <方法一> 中的列表存储
                寻找元素的速度: 列表: O(n) 字典: O(1)

            2. 长度的计算并不一定要等到发现'重复序列'后再计算!
                可以每次都计算, 这样当发生"s中无重复序列"的情况时, 也可以应对

        问题:
            1. 如何精准,快速计算 '两个指针之间的长度'??    <<+-1的问题>>
                i. 先定义长度的涵义: 长度计算是否包含"左右指针的所在位置"
                ii. 计算:
                    1. 如果只包含其中一个指针的位置: right_index - left_index
                    2. 两个指针都包含: right_index - left_index + 1
                    3. 两个指针都不包含: right_index - left_index - 1
                    (个人之前碰到这类问题要反应一段时间, 建议直接背下来套用)

        想法:
            1. 有时候, 没有必要为了一些极少数的输入,比如"", " "值, 而强行统一'判断标准'.
               其实完全可以当做特殊情况, 单独return一个数就行
               (我看LeetCode上很多朋友的题解都是这样, 特殊情况单独return的...)
    """
    def lengthOfLongestSubstring(self, s: str) -> int:
        longest_length = 0 # 记录'最长序列'的长度
        left_index = 0
        d = {}
        for right_index, e in enumerate(s):
            # 取出d中记录的 'e元素' 的上一个重复元素的索引位
            index = d.get(e)
            if index is not None: # 如果找到了d中的索引位, 说明"发现了重复序列"
                ### 解释: 窗口, 指当前左右指针范围内的元素
                if index >= left_index: # 当'上一个重复元素的索引位' 在 "左指针" 的右边时: 指当前记录的'窗口'中发现了'重复'
                    left_index = index + 1 # 将"左指针"移到 "上一个重复元素的右边位置"
                else: # 当'上一个重复元素的索引位' 在 "左指针" 的左边时: 并不是在当前的'窗口'发现'重复', 所以这个元素可以当做新元素添加到'd'中
                    d[e] = right_index
            # 强调: (本题的关键) [这个长度的计算纠结了很久]
                ### 因为for循环中的 right_index 的最大值是 len(s)-1, 所以想要包含最后一个字符, 必须把'right_index'向右平移1
                ### 即: 把 (right_index+1) 看成是一个整体  [可能有点绕, 如果有更好的解释, 欢迎交流~~]
            this_length = (right_index+1) - left_index
                # 每一次循环都去比较最大值 (虽然增加了计算量, 但在'没有重复序列的情况'可以正常计算最长长度)
            longest_length = max(this_length, longest_length)
                # 在d中更新每个新扫描到的元素的 '索引位'
            d[e] = right_index
        return longest_length
        # 执行用时 : 68 ms , 在所有 Python3 提交中击败了 80.39% 的用户
        # 内存消耗 : 13.9 MB , 在所有 Python3 提交中击败了 5.30% 的用户
        "正常来说这个算法的时间复杂度会低很多, 但是结果并不如人意...期待有缘人来告诉我..."




s = ""
s = " "
s = "pwwkew"
s = "dvdf"
res = Solution().lengthOfLongestSubstring(s)
print(res)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大枫free

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值