因此,为了更加高效地完成这个过程,我们可以借助一个 map ——保存字符最近一次的地址。当子串因为添加一个字符发生重复时,从 map 中拿出新增字符最近一次的地址(索引),并将窗口的左边界直接移动到该地址的下一个字符,则此时窗口中的子串不再包含重复的字符。
Java 实现 class Solution { public int lengthOfLongestSubstring(String s) { int ans = 0; Map<Character, Integer> map = new HashMap<>(); for (int l = 0, r = 0; r < s.length(); ++r) { char c = s.charAt(r); if (map.containsKey(c) && l <= map.get(c)) { l = map.get(c) + 1; } else { ans = Math.max(r - l + 1, ans); } map.put(c, r); } return ans; } } Python 实现 class Solution: def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ l, max_len = 0, 0 chars_has_seen = dict() for r, char in enumerate(s): if char in chars_has_seen and l <= chars_has_seen[char]: l = chars_has_seen[char] + 1 else: max_len = max(r - l + 1, max_len) chars_has_seen[char] = r return max_len 复杂度分析时间复杂度:\(O(n)\),其中 \(n\) 表示字符串的长度
空间复杂度:\(O(\min(n, \,m))\),其中,\(n\) 表示字符串的长度,\(m\) 表示字符集中字符的数目
解法四:滑动窗口(已知字符集) 思路假设我们现在已经知道所有可能出现的字符,比如 ASCII 字符集,那么就可以用大小固定的数组代替 map 去存储字符的位置。
Java 实现 public class Solution { public int lengthOfLongestSubstring(String s) { int ans = 0; int[] index = new int[128]; for (int l = 0, r = 0; r < s.length(); ++r) { l = Math.max(index[s.charAt(r)], l); ans = Math.max(r - l + 1, ans); index[s.charAt(r)] = r + 1; } return ans; } } Python 实现 class Solution: def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ all_chars = [0 for _ in range(128)] l, max_len = 0, 0 for r, char in enumerate(s): l = max(all_chars[ord(char)], l) # 得到窗口的左边界 max_len = max(r - l + 1, max_len) # 保留最长长度 all_chars[ord(char)] = r + 1 # 添加/更新字符的位置 return max_len 复杂度分析时间复杂度:\(O(n)\),其中 \(n\) 表示字符串的长度
空间复杂度:\(O(\min(n, \,m))\),其中,\(n\) 表示字符串的长度,\(m\) 表示字符集中字符的数目