题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
拿起小本本记一下子串和“子序列"的区别
总体思路
我觉得这一题能想到的思路有两种,一种是传统的”暴力破解“,虽然这种方式在很多情况下不是有效的,但是却是最容易想到的,而且,就本题而言,暴力破解也奇迹般地通过了测试,还有一种就是在暴力破解的方法上进行升级,去除暴力破解方法中的很多重复计算,从而使得算法效率有了一个很高的提升。
暴力破解
对于这个题目来说,暴力破解就是说要获得给定字符串的所有子串,从中找出最长的即可,具体实现起来也并不困难,只需要用两层循环,外层循环用来遍历子串的起始位置,而内层循环用来遍历子串的终止位置,但是需要注意的就是在遍历终止位置时需要注意的是当终止位置中的元素已经在已有子串中出现过时就可以终止,所得子串即为改起始位置下的最长子串。
def get_max_sub_str(s):
max_len = 0
for index,i in enumerate(s):
max_str = i
for j in s[index + 1:]:
if j not in max_str:
max_str += j
else:
break
if len(max_str) > max_len:
max_len = len(max_str)
return max_len
时间复杂度是最坏情况下程序的运行时间随着问题规模增长而增长的快慢,对于以上程序而言,显然最坏情况就是内层循环每次都要走到最后,基本语句的执行时间为
n
−
1
+
n
−
2
+
.
.
.
1
=
n
(
n
−
1
)
/
2
n-1 + n-2 + ...1 = n(n-1)/2
n−1+n−2+...1=n(n−1)/2,因此时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)
但是依然侥幸通过了测试
滑动窗口
虽然暴力破解已经通过了测试,但是这显然不是本题要考察的真正意图,而且作为一个追求完美的男人,怎么能就这样敷衍了事呢,于是我去看了leetcode的官方题解,看到这个方法忽然觉得恍然大悟。
上面的暴力破解的方法其实是存在着很多重复运算的,比如对于字符串"abcbcd"来说,我们以生成以a为起始位置的子串时,会得到”abc“的不重复子串,此时”abc“是显然不重复的,但是当我们生成以b为起始位置的子串时我们又会重新生成"bc",“bcd”,显然这里的”bc“和上一步产生的”abc“是重复的,也就是说任意一个已生成不重复子串的子串必定也是不重复的,因此我们的滑动窗口的方法就是为了解决这一问题而产生的。具体就是使用两个指针,左指针指向子串的最左端,而右指针指向我们已生成子串的最右端,不必重复移动,可以借助已有的子串生成以不同的起始位置生成的最大字串。
def get_max_sub_str(s):
max_len = 0
right_i = -1
n = len(s)
s_set = set()
for i in range(n):
if i == 0:
right_i += 1
s_set.add(s[i])
while right_i + 1 < n and s[right_i + 1] not in s_set:
right_i += 1
s_set.add(s[right_i])
max_len = max(max_len, len(s_set))
s_set.remove(s[i])
return max_len
总结
本题要注意借助这种滑动窗口的思想去减少大量的重复计算,这对提高程序的运行效率是很有帮助的。
这下真的要开始自己的刷题之旅了,面对即将到来的秋招,显得有些手足无措,虽然时间很紧张,但是还是要尽量挤出一些时间来刷题的,无论是从事什么岗位的工作,算法是基础,希望能顺利通过招行的Fintec训练营的笔试,孩子也想坐轮船。