题目描述:给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
【Python3实现】
例如,s="ababcdeff "中,最长子串“abcdef”长度为6;
简单分析:长度为 n 的字符串,有 n(n+1)/2 个子字符串 ,复杂度为 O(n*n), 判断长度为 n 的字符串是否有重复字符的次数是(1+2+3+4+5+6+...+ n-1),复杂度为O(n)。所以暴力法解决的复杂度为O(n*n*n)。
与队列类似,用到双指针。比如例题中的 ababcdeff,进入这个队列(窗口)为 ab 满足题目要求,当再进入 a,队列变成了 aba,这时候不满足要求。所以,j 要向右移动这个队列,利用指针 i+1 指向前一个重复字符的后面位置,即左指针指向b,计算队列长度 j-(i+1)+1 ,更新结果。所以关键在于如何判断重复和找到重复字符的下一个位置。
方法一:滑动窗口+哈希表
哈希表dic{ }统计:
指针 j 遍历字符,将每个字符和它的位置所组成的键值对录入dic{ }中,并在j遍历的时候,实时更新键值对,目的是利用哈希表统计字符 s[j] 的最后一次出现的索引(如果有重复,统计的索引就是前一个重复字符的位置),用于更新左指针i。
更新左指针i:
根据上一轮左指针 i 和dic,每轮更新左指针 i , 让区间 [i+1,j] 内无重复字符达到最大。更新方法, i = max( dic[s[j]] , i )
更新结果 res:
取上一轮的res 和本轮双指针区间[i+1,j]的宽度(j-i)最大值。更新方法,res = max( res,j-i)
过程记录:
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
复杂度分析:
时间复杂度O(n) 空间复杂度O(128)=O(1)
方法二:动态规划+哈希表
状态定义: 设动态规划列表 dp ,dp[j] 代表以字符 s[j]为结尾的 “最长不重复子字符串” 的长度。利用j 来遍历,设s[j] 左边距离最近的相同字符为s[i]
控制条件:dp[j-1] < j - i dp[j]= dp[j-1]+1 ,(现在遍历的是没出现过的字符) dp[j-1] >= j - i dp[j]= j-i ,(当前遍历的是已经出现过的字符,移动指针i后的长度,就是当前的dp[j]的值)
用一个变量tmp来记下当前dp值
哈希表统计各字符最后一次出现的索引位置、遍历到s[j]时,可以通过访问哈希表dic[s[j]]获取最近相同字符的索引i.
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]的话,就返回-1
dic[s[j]] = j # 更新哈希表
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])#dp是以s[j]为结尾的最长子串的长度
return res
复杂度分析:
时间复杂度O(n) 空间复杂度O(128)=O(1)