子串子数组的题很重要的一个切入点是:遍历数组,以某个字符为结尾,然后往左推!!!!
(知识点:用数组要比哈希表要快一点,不过都是常数时间)
然后就衍生出模拟法(其实就是肉眼找规律法)和动态规划法。
模拟法:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if len(s) == 0:
return 0
res = 1
for i in range(1, len(s)):
hashset = set()
temp = 1
hashset.add(s[i])
k = 1
while i - k >= 0:
if s[i-k] not in hashset:
hashset.add(s[i-k])
temp += 1
k += 1
else:
break
res = max(temp, res)
return res
动态规划法:
动规的思考模式就是该位置的值取决于什么因素,找出来!!!
动规的题目某个位置的值肯定取决于之前的位置,很多题都是取决于上一个位置的值。
这题的思路是,某个位置结尾的最长长度,取决于两个因素,一个是当前位置的字符出现在之前字符的位置,还有就是上一个位置的字符往前回去的最长长度(已经求出)。两者的较小值,则为该位置字符为结尾的最长长度。
换种说法,也就是当前位置能往左推最多能推到哪个位置,取决于两个因素,一个是:当前字符在上一次出现在哪;还有一个是:上一个字符结尾,往左最多推到哪(动规已求)
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# 动态规划法
# 但这题没有用dp数组,用dp数组就是先把每一个位置结尾的最长字符长度求出来,再遍历dp数组取最大值
# 但我可以优化,首先dp数组可以存每个位置结尾的往左推最长字符长度的位置
# 然后直接拿两个变量分别记住上一个字符结尾的往左推最大长度的位置(pre)和记住全局最长长度(length)。就不需要dp数组了。
# 就求出当前位置往左推最多能推到的位置了。同时当前长度也可以算出,和length比较取较大值
if s == "":
return 0
lis = [-1] * 256
length = 0
pre = -1 # 上一个字符结尾,往左推推不动的位置
cur = 0
for i in range(len(s)):
pre = max(pre, lis[ord(s[i])]) # lis[ord(s[i])]代表当前字符首先不能重复,pre代表之前的字符也不能有重复。
# 取max确保了当前字符和之前字符都不重复
cur = i - pre
length = max(length, cur)
lis[ord(s[i])] = i # 这个lis可不是dp数组,这只是记录当前字符的位置,以供查看有没有重复的。可以用set代替
return length