jz048.最长不含重复字符的子字符串

 

题目链接

我们不难找出字符串中的所有子字符串,然后可以判断每个子字符串中是否包含重复的字符,但是这种蛮力法效率太低。对于一个长度为n的字符串,其含有O(N^2)个字串,我们需要O(N)的时间来判断一个字符串中是否包含连续字串。因此暴力法的总时间效率是O(N^3)。

接下来我们用动态规划算法来提高效率,首先定义函数f(i)表示以第i个字符为结尾的不包含重复字符的子字符串的最长长度。我们从左到右逐一扫描字符串中的每个字符。当我们计算以第i个字符为结尾的不包含重复字符的子字符串的最长长度f(i)时,我们已经知道f(i-1)了。

如果第i个字符之前没有出现过,那么f(i)= f(i-1)+ 1,例如,在字符串“arabcacfr”中,显然f(0)等于1,在计算f(1)时,下标为1的字符“r”之前没有出现过,因此f(1)等于2,即f(1)=f(0)+ 1。到目前为止,最长的不含重复字符的子字符串是“ar”。如果第i个字符出现过,那情况就要复杂一些了,我们先计算第i个字符和它上次出现在字符串中的位置的距离,并记为d,接着分两种情形分析。第一种情形是d小于或者等于f(i-1),此时第i个字符上次出现在f(i-1)对应的最长子字符串中,因此f(i)= d。同时这也意味着在第i个字符出现两次所夹的子字符串中再也没有其他重复的字符了。在前面的例子中,我们继续计算f(2),即以下标为2的字符“a”为结尾的不含重复字符的子字符串的最长长度。我们注意到字符“a”在之前出现过,该字符上一次出现在下标为0的位置,他们之间的距离d为2,也就是字符“a”出现在f(1)对应的最长不含重复字符的子字符串“ar”中,此时f(2)= 2,对应的最长不含重复字符的子字符串是”ra“。第二种情形是d大于f(i),此时第i个字符上次出现在f(i-1)对应的最长子字符串之前,因此仍然有f(i)=f(i-1)+ 1。我们接下来分析以字符串“arabcacfr”最后一个字符“r”为结尾的最长不含重复字符的子字符串的长度即求f(8)。以它前一个字符“r”为结尾的最长不含重复字符的子字符串的长度,即求f(8)。以它前一个字符“f”为结尾的最长的不含重复字符的子字符串是“acf”,因此f(7)= 3。我们注意到最后一个字符“r”之前在字符串“arabcacfr”中出现过,上一次出现在下标为1的位置,因此两次出现的距离d等于7,大于f(7)。这说明上一个字符“r“不在f(7)对应的最长不含重复字符的子字符串”acf“中,此时把字符”r”拼接到“acf”的后面也不会出现重复字符。因此f(8)= f(7)+ 1,即f(8)= 4,对应的最长不含重复字符的子字符串是“acfr”。

这个算法的第二种算法的实现时间复杂度是O(N),空间复杂度是O(1)。

class Solution:
    def lengthOfLongestSubstring1(self, s):
        if not s:
            return 0
        letters = [chr(x) for x in range(0, 128)]
        dic = dict.fromkeys(letters, -1)
        res = [0]*len(s)
        res[0] = 1
        dic[s[0]] = 0
        for i in range(1, len(s)):
            if dic[s[i]] == -1:
                res[i] = res[i-1] + 1
            else:
                if i - dic[s[i]] <= res[i-1]:
                    res[i] = i - dic[s[i]]
                else:
                    res[i] = res[i-1] + 1
            dic[s[i]] = i
        return max(res)

    def lengthOfLongestSubstring2(self, s: str) -> int:
        dic = {}
        res = tmp = 0
        for j in range(len(s)):
            i = dic.get(s[j], -1)
            dic[s[j]] = j
            tmp = tmp + 1 if tmp < j - i else j - i
            res = max(res, tmp) # max(dp[j - 1], dp[j])
        return res