题目:
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.
原始思路:
首先想到遍历的方法,从左往右遍历字符串,给出每个位置对应的最长子字符串长度。为了不用每次都重新遍历,需要一个辅助字符串来帮助判定。代码如下:
class Solution(object): def lengthOfLongestSubstring(self, s): max_len = 0 tmp = '' for char in s: if char not in tmp: tmp += char max_len = max(max_len, len(tmp)) else: tmp = tmp[tmp.find(char) + 1:] + char return max_len
假设字符串长度为n, char从左到右迭代了n次。每次迭代需要判断char是否在tmp中。鉴于字符串的存放并没有使用hash方法,这一查找的时间复杂度为O(k),k为最长不含重复字符的子字符串长度。故总的时间复杂度为O(kn),空间复杂度为O(k)。这样的时间复杂度还是有些高。
优化:
既然字符串的in查找时间复杂度比较高,想到可以快速查找的集合和字典数据结构。因为这两种数据结构是无序的,因此需要定义一个左指针和右指针,去描述子字符串的位置。
若使用集合,当遍历的char不在集合中时,添加元素很方便;当遍历的char在集合中时,则需要更新左指针的位置,并删除集合中的元素。具体删除方法是不断右移左指针,并删除左指针在集合中对应的元素。知道左指针指向的元素和右指针指向的元素相同。时间复杂度为O(n)
这种方式需要删除集合中元素,仍比较麻烦。参考leetcode讨论区@cbmbbz的解答,发现可以用词典存储字符及其对应的位置,由此更新左指针只需将左指针和重复字符出现的位置进行比较即可,也无需删除字典中元素。时间复杂度也是O(n)(具体应该比上一种方法快一些)。代码如下:
class Solution(object): def lengthOfLongestSubstring(self, s): left, max_len = 0, 0 index_dict = {} for right, char in enumerate(s): if char in index_dict: left = max(left, index_dict[char] + 1) index_dict[char] = right max_len = max(max_len, right - left + 1) return max_len