. 题目
. 审题
今天这道题,看起来是不是很简单?
但做为一道中等难度的题目,它可不会让你失望。敲起你的键盘,试着来解下这道题,你会很难找到一个好的思路。
事实上,想这个思路也确实花了我不少的时间,是写代码时间的好几倍。
首先,要理解 子串
和 子序列
的区别。
子串:必须同时具备,连续性和唯一性。
子序列:只须具备唯一性即可。
. 我的版本
先说下我的思路。
假设一个字符串的长度是10,那我就先从字符串的[0,1]子串查起,假如子串里没有重复字符(通过set()
去重查看),就继续查看子串[0,2],如果还是没有重复,就继续查看[0,3],这时候,我们发现这个子串里有重复字符(比方说,子串"abcb"),接下来,我们就要找出是在重复的那个字符的索引(查出是 b,在索引 1 处)。那下次我们查找的子串就不是[0,5]了,而是[2,5],就这样一直往下,直到遍历完整个字符串。
class Solution:
def lengthOfLongestSubstring(self, s):
if len(s) == 1:
return 1
reset_start= False
start = 0
max_len = 0
for i in range(len(s)):
# reset_start就为True,需要重新设置起点
if reset_start:
start = new_start
# 为什么加1,是因为第一次start会和end一样是0
end = i + 1
sub_str = s[start:end]
len_sub_str= end - start
if len(set(sub_str)) != len_sub_str:
# 找出是在哪个位置重复
rep_index = sub_str.index(s[i])
new_start = rep_index + start + 1
reset_start= True
continue
if len_sub_str > max_len:
# 记录下迄今为止最在长度
max_len = len_sub_str
skip = False
return max_len
运行一下,结果很差。只击败了24.73%
。今天吃不了鸡腿了。不过小明真的是尽力了。只能想到这个思路。
. 网上的版本
按照惯例,还得上网去学习别人的优秀代码。
真是惊叹,果然是思路决定出路啊。
这种解法很巧妙。
定义两个变量longest
和left
,longest
用于存储最长子字符串的长度,left
存储无重复子串左边的起始位置。
然后创建一个哈希表,遍历整个字符串,如果字符串没有在哈希表中出现,说明没有遇到过该字符,则此时计算最长无重复子串,当哈希表中的值小于left,说明left位置更新了,需要重新计算最长无重复子串。每次在哈希表中将当前字符串对应的赋值加1。
class Solution(object):
def lengthOfLongestSubstring(self, s):
longest = 0; left = 0; tmp = {}
for index, each in enumerate(s):
if each not in tmp or tmp[each] < left:
# 计算当前最长的长度
longest = max(longest, index - left + 1)
else:
left = tmp[each]
tmp[each] = index + 1
return longest
运行一下,看看吧,击败了92.3%
。众望所归啊。 佩服佩服。
. 总结
其实我的思路,和上面那个优秀代码的思路是一致的。
我做得不好的一点是,在检测当前子串是否重复这一点上面,我选了一个效率非常低的做法,就是每次循环都要计算下len(set(str_obj))
和 len(str_obj)
,而这种是相当耗时的,而且会随着字符串长度的增长,耗时也线性增加。
而聪明的人,则是通过维护一个字典,来存放唯一值,和唯一值的最大索引。对执行速度的提升,可以说是非常显著的。