48. 最长不含重复字符的子字符串
方法一:动态规划+哈希表
把原问题拆解为:以每个字符为右边界的最大子串(每次计算都是基于前一个字符的结果)
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic = {} # 哈希表用来保存每个字符出现的最近的位置
res = tmp = 0
for j in range(len(s)): # 遍历每一个字符
i = dic.get(s[j], -1) # 获取字符上一次出现的位置,索引i
dic[s[j]] = j # 更新哈希表
tmp = tmp + 1 if tmp < j - i else j - i # tmp用来保存dp[j-1],dp[j - 1] -> dp[j]
res = max(res, tmp) # max(dp[j - 1], dp[j]) 更新最长子串长度
return res
- 复杂度分析:
- 时间复杂度 O ( N ) O(N) O(N): 其中N为字符串长度,动态规划需遍历计算dp列表。
- 空间复杂度$O(1) : 字 符 的 A S C I I 码 范 围 为 0 127 , 哈 希 表 d i c 最 多 使 用 : 字符的 ASCII 码范围为0~127 ,哈希表dic 最多使用 :字符的ASCII码范围为0 127,哈希表dic最多使用O(128) = O(1)$大小的额外空间。
方法二:动态规划 + 线性遍历
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
res = tmp = i = 0
for j in range(len(s)):
i = j - 1
while i >= 0 and s[i] != s[j]: i -= 1 # 线性查找 i
tmp = tmp + 1 if tmp < j - i else j - i # dp[j - 1] -> dp[j]
res = max(res, tmp) # max(dp[j - 1], dp[j])
return res
- 复杂度分析:
- 时间复杂度 O ( N 2 ) O(N^2) O(N2):其中 N N N为字符串长度,动态规划需遍历计算dp列表,占用 O ( N ) O(N) O(N);每轮计算 d p [ j ] dp[j] dp[j]时搜索 i i i需要遍历 j j j个字符,占用 O ( N ) O(N) O(N) 。
- 空间复杂度 O ( 1 ) O(1) O(1): 几个变量使用常数大小的额外空间。
方法三:双指针 + 哈希表
更新左指针 i i i: 根据上轮左指针 i i i和 d i c [ s [ j ] ] dic[s[j]] dic[s[j]] ,每轮更新左边界 i i i,保证区间 [ i + 1 , j ] [i+1,j] [i+1,j]内无重复字符且最大。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic, res, i = {}, 0, -1
for j in range(len(s)):
if s[j] in dic:
i = max(dic[s[j]], i) # 更新左指针i
dic[s[j]] = j # 哈希表记录
res = max(res, j - i) # 更新结果
return res