求最长无重复字符子串的长度。
未标记
考查重点:滑动窗口与重复位置更新。滑窗记录字符最近位置,重复落在窗口内就将左端移到其后一位,左端只前进并更新最大长度
本题目录
参考答案
核心回答
用滑动窗口加字符最近出现位置表,维护一个没有重复字符的连续区间。右端逐个加入字符;如果这个字符上一次出现在当前窗口内,就把左端移到上次位置之后,再更新最大长度。
关键是左端只能前进:left = max(left, 上次位置 + 1)。这样既能去掉当前重复,又不会因为遇到窗口外的旧字符而退回。哈希表操作按平均常数时间计算,整体时间为 O(n),额外空间为 O(min(n, 字符集大小))。
题目求的是连续的子串长度。例如 pwwkew 的答案为 3,wke 连续且无重复;pwke 虽然无重复,却不是连续子串。
Python 实现
def length_of_longest_substring(s: str) -> int:
last_seen = {}
left = 0
best = 0
for right, char in enumerate(s):
left = max(left, last_seen.get(char, -1) + 1)
last_seen[char] = right
best = max(best, right - left + 1)
return best
last_seen 记录字符在已经扫描部分中最近一次出现的位置,不只记录当前窗口;因此移动左端时需要取 max。没有出现过的字符使用 -1,得到的候选左端是 0。
这里按 Python 字符串的 Unicode 码点处理,字典不限定为 128 个 ASCII 字符。输入是 str;若输入为字节流,应先按正确编码解码。
为什么遇到重复时这样移动
处理 right 前,当前窗口内的字符互不重复。新字符若上次位于 previous,有两种情况:
previous < left:旧字符已在窗口外,不影响当前窗口,左端保持原位。previous >= left:要排除旧字符,新的合法窗口必须从previous + 1或更靠右开始;取恰好这个位置,可以保留尽量长的合法区间。
因此每处理一个右端,都得到以它结尾的最长无重复窗口;遍历所有右端并取最大值,就覆盖了全局最优答案。
abba 能检查左端回退这个常见错误:读到第二个 b 时,左端移动到下标 2;最后读到 a,它上次在下标 0,已经在窗口外,所以左端仍应为 2。若直接赋值为 1,就会把 bba 错算成合法窗口。
复杂度和实现细节
右端恰好扫描 n 次,每步只做字典读写和常数次比较,不会重新扫描整个窗口,因此在哈希操作平均 O(1) 的条件下,时间为 O(n)。字典最多保存输入中出现过的不同字符,空间为 O(min(n, 字符集大小))。
另一种写法是维护窗口内字符计数,重复时用 while 逐个移出左端字符;两个指针都只前进,总移动次数仍为 O(n)。若在每一步切片并重新构造集合检查整段窗口,就会增加扫描和复制成本,不能再直接沿用这个复杂度。
边界样例
assert length_of_longest_substring("") == 0
assert length_of_longest_substring("a") == 1
assert length_of_longest_substring("aaaa") == 1
assert length_of_longest_substring("abba") == 2
assert length_of_longest_substring("pwwkew") == 3
assert length_of_longest_substring("你好你世") == 3
assert length_of_longest_substring("🙂a🙂") == 2
空格和标点也是字符。若需求按用户看到的字形计算,例如组合音标或由多个码点构成的表情,应先明确规范化与字素分割规则;本实现比较码点,不会自动把这些序列当成一个字符。
常见追问
怎样返回具体子串? 更新 best 时同时保存起点,结束后只切片一次。若多个子串同样长,可以按约定保留最早出现的一个。
为什么不能排序后去重? 排序会破坏原字符串的连续性;去重得到不同字符总数,也无法反映它们能否共同位于某个连续区间。
参考资料
- 国内:JavaGuide:双指针与滑动窗口:借鉴可变窗口的加入、修复与更新顺序,说明正文中窗口内始终无重复的不变量。
- 国内:labuladong:滑动窗口算法核心代码模板:依据单向指针和窗口收缩的分析,解释正文中左端不能回退及嵌套收缩仍为线性时间的原因。
- 国内:Doocs LeetCode Wiki:无重复字符的最长子串:参考计数收缩解法,说明正文中它与最近位置跳转法维护同一合法窗口的不同方式。
- 国外:USACO Guide:Two Pointers:借鉴连续区间中单向指针的摊还分析,解释正文中收缩循环总移动次数为 O(n) 的复杂度。
- 国外:Python:Unicode HOWTO:依据 Unicode 码点、编码和组合字符的区别,界定正文实现的字符单位及字形需求需要额外处理的条件。
相关面经
16 篇懂车帝
后端开发工程师算法:无重复字符的最长子串(Hot100,mid)
科大讯飞、首都在线、宇量晟、四川长虹、度小满、字节跳动、恒生电子、腾娱互动、金蝶、汽车之家
三面:是个中登,看起来是个大牛。因为是全栈开发,问了很多架构层面怎么设计,场景题偏多。一个索引题,在给的字段和查询条件下建立合适的索引。手撕无重复长度字符串的最长子串。
字节跳动
Agent 算法工程师做题:最长无重复字符子串长度
深信服
Agent 开发工程师无重复字符的最长子串
阿里巴巴
AI 全栈开发工程师手撕1-无重复字符的最长子串
展开剩余 11 篇面经
百度
AI Infra算法题:给定一个数组,求最长无重复元素子数组长度。
字节跳动
后端开发工程师简单题, 最长连续不重复子串。
点点互动
Agent 开发工程师算法: 最长无重复子串
字节跳动
Agent 测评手撕:无重复的最长子串
百度
后端开发手撕是无重复字符的最长子串,滑动窗口。
滴滴
国际化后端开发算法:最长无重复子串
阿里巴巴
大模型算法工程师最长无重复子串
阿里巴巴
大模型算法工程师无重复字符最长子串问题
字节跳动
后端开发工程师(AI / Agent 方向)手撕最长无重复字符子串
快手
手撕:无重复字符的最长子串
轻舟智航
Agent 开发工程师(自动驾驶工具链)最长不重复子串





