LeetCode(3) Longest Substring Without Repeating Characters

求一个字符串里最长连续不重复子串。 O(n2) 的算法肯定会超时,但是思想有一定借鉴意义。


O(n2) 的算法思路:

指示器j,用一个长度为n的列表flag,初始化置为0,flag[a]=b表示在s[0]到s[a]的子串中最长连续不重复子串是从s[b]到s[a]。设置计数器maxlen=0, 表示目前最长的连续不重复子串的长度为maxlen。那对于s[j]来说,从s[j-1]开始回溯遍历至s[0],如果s[j]与s[k]重复了,那么说明在从s[k+1]到s[j]的子串是连续不重复的,设置flag[j]=k+1,然后maxlen=max(maxlen, j-k)。

从这个思路我们会发现,如果这个字符串本身就是没有重复的,那么我们每次回溯都会回溯到字符串的头部,flag全部为0。那么如何简化这个算法呢?可以发现,这个算法在判断一个字符是否在一个子串中出现的时候,是通过遍历这种 O(n) 的方法实现的。那如果把它替换成 O(1) 的方法,即可减少时间复杂度。我们可以设计一个新的列表flag,表示在最长连续不重复子串里某个字符是否出现。出现则flag[该字符对应ACSII码]=1,反之为0。它的长度为255,利用python里面的ord()方法将字符转换成对应的ASCII码。
新的问题又来了,我们依然需要先把flag置为0,然后每次从s[j]遍历回s[k],把flag对应的位置重新置为1。那有没有更好的办法呢?通过分析可以发现上面这个方法,其实相当于做了很多无用功。举个极端的例子,如果字符串本身就是连续不重复的,那么我们每一次都会遍历一次从s[0]到s[j-1]的子串去把flag重新标注一次。但是标注并没有改变flag的任何值,因为对应位置都已经是1了。所以无用功就在这里。

O(n) 的算法思路:

设置一个新的列表flag,表示在最长连续不重复子串里某个字符是否出现。出现则flag[该字符对应ACSII码]=1,反之为0,其长度为255。两个指示器i,j,表示从s[i]到s[j-1]的子串是目前从s[0]到s[j-1]的字符串中最长的连续不重复子串。那对于s[j]来说,如果s[j]与从s[i]到s[j-1]的子串里的字符不重复,那么从s[i]到s[j]的子串是目前从s[0]到s[j]的字符串中最长的连续不重复子串,flag[ord(s[j])]=1。如果中间s[j]与s[k]重复,那么从s[i]到s[k]的字符应该全部丢弃,去flag里面把它们对应的位置的值置为0即可。我们会发现这个过程中,i,j各遍历了只整个字符串一次。所以复杂度降到了 O(n)

下附AC代码:

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        flag = [0 for i in range(256)]
        str_len = len(s)
        maxlen = 0
        i,j =0 ,0
        while j<str_len:
            if flag[ord(s[j])] == 0:
                flag[ord(s[j])] = 1
            else:
                while i<j:
                    if s[i]!=s[j]:
                        flag[ord(s[i])] = 0
                        i+=1
                    else:
                        i+=1
                        break
            j+=1
            maxlen = max(maxlen ,j-i)
        return maxlen

猜你喜欢

转载自blog.csdn.net/gzhermit/article/details/78855283