原题:
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)