题目重点:用最近位置跳过重复字符,证明窗口不变量,处理abba、空串和Unicode字符定义。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 按Python Unicode code point判断字符相同;连续子串,不允许跳过中间字符。
- 只返回长度,空串返回0;不做大小写或Unicode归一化。
- 若需要返回字符串,同长度取最早起点可另保存best_start,最终一次切片。
解题路线
- 维护窗口[left,right]和每个字符最近一次出现位置last。处理text[right]时,如果这个字符此前出现过,则窗口左端至少要移到last[char]+1,才能删除上次出现。
- 更新必须写left=max(left,last[char]+1),因为上次位置可能已经在窗口外。对abba,处理末尾a时last[a]=0,但left已是2,若直接赋1会倒退并把两个b重新包含。
- 不变量是每次处理完当前字符,窗口内没有重复,且left是以right结尾的无重复子串的最小合法左端。旧窗口已无重复,只会新增当前字符这一个潜在重复,跳过它上次出现即可。
- 最小合法左端对应当前right的最长窗口,遍历所有right并取最大长度便覆盖全局最优。更新长度是right−left+1,随后记录last[char]=right以服务未来字符。
- 每个右端只处理一次,左端仅向前跳,不重新扫描窗口。哈希表查询更新平均O(1),因此总体O(n);需要固定ASCII时可换固定数组,但不能在未知字符域中擅自用256长度数组。
- 若采用集合版本,则重复时循环逐个移走左字符直到唯一;每个字符最多入集合、离集合各一次,也为线性。最近位置版本一步跳转更简洁,但仍应讲清max如何维护不变量。
- 返回具体子串时保存坐标而非每次切片;实时流可以保留索引和所需窗口数据,想在流结束后输出最长字符串还需保存内容或外部引用。Unicode组合音标/emoji连字若按用户感知字符比较,需要先做grapheme分段。
手动走一遍
abba:窗口依次a、ab、b、ba,答案2。pwwkew答案3,可取wke或kew;pwke是子序列而不是连续子串。🙂你🙂好的答案3,空串0。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def longest_unique_length(text):
left, best = 0, 0
last = {}
for right, char in enumerate(text):
left = max(left, last.get(char, -1) + 1)
best = max(best, right - left + 1)
last[char] = right
return best
复杂度
平均O(n)时间;last字典O(U)空间,U为输入不同字符数,最坏O(n)。固定有限字符域时可称O(字符域大小),不能在任意Unicode输入下笼统声称O(1)。
易错点与追问
容易失分的边界
- 重复字符在窗口外仍把left回退。
- 把连续子串写成去重子序列。
- 长度漏掉+1。
- 每次保存答案都复制整段字符串。
继续深挖
- 最多允许K个不同字符如何修改?
- 最长子串中每个字符最多出现两次怎样维护?
- 如何返回全部同长最优窗口?
- 流式输入返回长度和返回内容分别需要什么存储?
