一、题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、题解
方法一
思路:
使用遍历字符串同时记录该子串之前遇到的字符与之前的子串长度,若遇到重复字符则更新最大无重复字串的长度并且将当前字串长度重置重新开始从上一次遇到重复字符的后一个字符位置遍历字符串。
这其中最重要的就是查找当前遍历到的字符与该子串之前记录的字符是否重复,如果遍历查找则查找时间复杂度为O(n),所以采用哈希表,哈希表的查找时间复杂度为O(1)。
代码:
class Solution():
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
current_len=0
memory_num={}#记录子串
max_len=0
header_pointer=0
while(header_pointer<len(s)):
for i,each_char in enumerate(s[header_pointer:]):
#若当前字符没重复
if(memory_num.get(each_char)==None):
memory_num[each_char]=i+header_pointer
current_len+=1
#如果遍历到底
if(header_pointer+i>=len(s)-1):
max_len=max(max_len,current_len)
return max_len
else:
#否则将指针移动到上次遇到该字符的位置后一个并且退出for循环
header_pointer=memory_num[each_char]+1
memory_num.clear()
#更新最长长度并且重置当前长度
max_len=max(current_len,max_len)
current_len=0
break
#若最后一个重复了
max_len=max(max_len,current_len)
return max_len
结果
时间消耗上结果并不理想
方法二(滑动窗口算法)
思路
在使用哈希表查找的基础上,维持一个滑动窗口,由左右指针指示。
相当于问从一个字符开始能获得的最长字串长度为多少,然后分别询问之后的字符从其开始的最长字串长度是多少,若长于已经记录的max_len就更新max_len
证明:若一个从字符的位置开始右指针右移直到移动到指向与当前字串有重复字符的位置,左指针不动,则左指针移动到当前左右指针中字串的那个重复字符索引+1的索引位置前,左指针指向的字符开始的最长字串一定小于最初的右指针位置减去左指针位置的值。
因为在左指针移动到当前左右指针中字串的那个重复字符索引+1的索引位置前,左指针的字符开始到右指针的前一个字符结束的字串一定有一个字符与右指针指向的字符重复,而左指针右移减少了左右指针之间的距离。
由于左右指针最多遍历一次字符串,时间复杂度为O(n)
代码
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
if(len(s)==0):
return 0
left_pointer=0
right_pointer=0
max_len=0
hashmap={}
while(right_pointer<len(s)):
if(hashmap.get(s[right_pointer])==None):
hashmap[s[right_pointer]]=0
right_pointer+=1
else:
del hashmap[s[left_pointer]]
left_pointer+=1
max_len=max(right_pointer-left_pointer,max_len)
return max_len
结果
可见时间上获得了巨大优化