LeetCode:3. Longest Substring Without Repeating Characters

LeetCode:3. Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

题目的意思就是求解一个字符串的最长无重复字符的连续子串。

思路一(暴力求解)

直接暴力求解,依次遍历字符串中的每个字符,以这个字符为起点向后查找,有重复的字符则直接跳过,没有重复则不断添加到一个临时列表sub中,每次比较sub的长度,得到一个最大值maxLen。

Python 代码实现

class Solution:
    
    def lengthOfLongestSubstring(self, s: str) -> int:
        maxLen = 0
        sub = []
        repeatIndex = 0
        for outter in range(len(s)):
            for index in range(outter,len(s)):
                try:
                    repeatIndex = sub.index(s[index])
                except:
                    repeatIndex = -1
                print(repeatIndex)
                if repeatIndex != -1:
                    if len(sub) >= maxLen:
                        maxLen = len(sub)
                    sub = []
                    break
                else:
                    sub.append(s[index])
                    if len(sub) >= maxLen:
                        maxLen = len(sub)
        return maxLen

复杂度分析

首先看最里面一层,判断index位置字符是否和sub的字符重复,所需时间是O(j-i)。然后外面一层,j 是从 i+1 处开始,到s的末尾,所以这两层的时间是 ∑ j = i + 1 n O ( j − i ) \sum_{j=i+1}^{n}O(j-i) j=i+1nO(ji)。最外面一层就是从0到n-1累加,于是最终的时间复杂度是: O ( ∑ i = 0 n − 1 ( ∑ j = i + 1 n ( j − i ) ) ) = O ( ∑ i = 0 n ( ( 1 + n − i ) ( n − i ) 2 ) ) = O ( n 3 ) O(\sum_{i=0}^{n-1}(\sum_{j=i+1}^{n}(j-i)))=O(\sum_{i=0}^{n}(\frac{(1+n-i)(n-i)}{2}))=O(n^3) O(i=0n1(j=i+1n(ji)))=O(i=0n(2(1+ni)(ni)))=O(n3)

思路二(滑动窗口)

对于暴力求解法,要遍历从i到n(i=0,1,…,n-1)的所有字串。这样就导致了 O ( n 2 ) O(n^2) O(n2)的复杂度了。然后再加上内部的O(n)的查找重复字符,这就导致了 O ( n 3 ) O(n^3) O(n3)的时间复杂度。我们如何优化呢?这里引入一个滑动窗口的概念。

滑动窗口这种理念经常在数组和字符串的算法问题中用到。一个滑动窗口就是从i到j的区间,通常是[i,j)左闭右开。比如我们向右移动一个元素,那区间就变成了[i+1,j+1),其实就是左边界和右边界同时加1,造成了一个滑动的效果。其实如果只有一个边界加1(或者减1),也可以叫滑动窗口。我们这个问题就是一个边界在变化。

考虑我们的问题,如果向右遍历的时候发现j位置和i位置的元素重复,那么就应该立即滑动到i+1处,也就是将左边界向右扩展,如果不重复,则将右边界向右扩展。

此外这里我们用set来保存遍历过程中的最大字串,这样判断一个字符是否在其中,只要O(1)的时间。

Python 代码实现

class Solution:
    
    def lengthOfLongestSubstring(self, s: str) -> int:
        maxLen = 0
        sub = set()
        lenS = len(s)
        i = 0
        j = 0
        while(i < lenS and j < lenS):
            if s[j] not in sub:
                sub.add(s[j])
                j+=1
                if len(sub) > maxLen:
                    maxLen = len(sub)
            else:
                sub.remove(s[i])
                i+=1
        return maxLen

复杂度分析

外面while循环仔细算一下,其实是O(2n)=O(n)的复杂度,循环里面刚刚已经说了是O(1),所以最终的时间复杂度是O(n)。

思路三:滑动窗口的优化

上面这个方法实际需要2n步,因为每次发现重复字符后,左边界只向右滑动一个位置,如果我们可以直接滑动到重复字符的位置,那效率又会提升。

Python 代码实现

class Solution:
    
    def lengthOfLongestSubstring(self, s: str) -> int:
        maxLen = 0
        lenS = len(s)
        i = 0
        j = 0
        charMap = {}
        while(i < lenS and j < lenS):
            try:
                if charMap[s[j]] is not None:
                    i = max(charMap[s[j]],i)
            except:
                print("")
            maxLen = max(maxLen,j-i+1)
            charMap[s[j]]=j+1
            j+=1
        return maxLen

这里这个charMap就是用来保存出现重复字符的最右位置,然后直接将左边界滑到这里。因为前面已经出现了和当前这个字符重复的字符,所以这之前的子串就不用再考虑了,这样就提升了效率。

思路四:使用ASCII 码

其实这个就是上面使用map的方法的变种。上面的方法是将每个重复字符的最右位置和其对应的字符保存到map中,而这里使用ASCII码的方法,要预先初始化包含所有ASCII码的最长列表(128),也就是每个位置对应的是一个字符,然后将重复字符的最右位置保存到该字符对应的ASCII码列表中的位置。

Python 代码实现

class Solution:
    
    def lengthOfLongestSubstring(self, s: str) -> int:
        n = len(s)
        ans = 0;
        index = [0] * 128; # current index of character
        i = 0
        j = 0
        # try to extend the range [i, j]
        while (i < n and j < n) :
            i = max(index[ord(s[j])], i);
            ans = max(ans, j - i + 1); 
            index[ord(s[j])] = j + 1;
            j+=1
        return ans;

THE END.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值