原題:
https://leetcode.com/problems/longest-substring-without-repeating-characters/description/
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.
分析:
首要要判斷non-repeating characters,考慮要用字典或集合。
遍歷全部子字符串需要O(N2),每個字符串判斷non-repeating chars需要O(n),所以最壞需要時間O(n3)和空間O(n)。但由于構建子字符串和使用字典判斷字符獨特性可以同時進行,所以brute force應該只需要O(n2)。
考慮先實現brute force然后進行優化。
解題:
第一版:Brute Force
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
res = 0
for i in range(len(s)):
letters = set()
j = i
while j < len(s) and s[j] not in letters:
letters.add(s[j])
j += 1
res = max(res, len(letters))
return res
Time Limit Exceeded
時間是O(n2),但仍不出所料的超時了。
第二版:從brute force剪枝,已經構建出的子字符串字典不用重復構建。使用ordered dict來實現一個滑動窗口,右側enqueue,左側dequeue。當需要enqueue的字符已經存在時,dequeue已存在的字符和它左邊的所有字符。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
res = 0
letters = collections.OrderedDict()
for letter in s:
inserted = False
while not inserted:
if letter not in letters:
letters[letter] = 1
inserted = True
res = max(res, len(letters))
else:
letters.popitem(last = False)
return res
Runtime: 389 ms
Accepted,但是速度仍然不夠理想。
使用ordered dict,在pop左側字符的時候是O(n)的時間。雖然比brute force有優化,但總時間依然是O(n2)。
第三版:在計算子字符串長度的時候,我們并不需要考慮每一個字符,只需要知道它的開始和結束的index就可以了。因此,我們實際需要的是一個字典,里面保存每一個已經遍歷過的字符的index(重復字符取最大值)。并且在每次遇到重復字符的時候,用字典里保存的index來計算新的開始index。這樣每次遍歷結束時,我們可以保證開始和當前index之間的子字符串是沒有重復字符的。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
res = start = 0
discovered = {}
for idx, val in enumerate(s):
if val in discovered:
start = max(start, discovered[val] + 1)
discovered[val] = idx
res = max(res, idx - start + 1)
return res
Runtime: 82 ms
時間:O(n)
空間:O(n)