题目重点:返回最长连续子串的位置,未出现的字符不参与计数。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 输入按Python Unicode code point计字符,不做大小写或Unicode归一化;正式要求若为字节或grapheme需换tokenizer。
- 返回半开区间;并列取最早起点,空串返回(0,0)。
解题路线
- 固定左端点,逐一扩展右端点,并维护当前窗口频次。只有所有正频次数值相等时才更新答案。零频字符不进入表。
- 这项性质不随窗口扩展单调变化。例如ab满足,aba不满足,abab重新满足。因此不能凭“当前不满足”移动左指针并声称普通滑窗成立。
- 基线枚举全部窗口,freq始终精确表示当前窗口。完整枚举保证不会漏掉先失效再恢复的更长答案。若字母表固定26,可维护频率的频率,减少每次校验成本。
手动走一遍
s="ababa":窗口ab、abab有效;ababa的a出现3次而b2次,无效。最长返回(0,4)。s="aaaa"时只有一个出现字符,次数4仍满足;空串没有非空窗口。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def longest_balanced(s):
best = (0, 0)
for left in range(len(s)):
freq = {}
for right in range(left, len(s)):
c = s[right]
freq[c] = freq.get(c, 0) + 1
if len(set(freq.values())) == 1 and right + 1 - left > best[1] - best[0]:
best = (left, right + 1)
return best
复杂度
一般有d种字符时O(n²d)时间、O(d)工作空间;固定字母表时O(n²)。返回下标不复制子串;最后切片需O(L)。
易错点与追问
容易失分的边界
- 不要把“所有字符频率相等”写成“每字符只出现一次”。
- 若用set(freq.values()),每次构造集合不是O(1),除非明确字母表为常数。
继续深挖
- 固定小字母表怎样维护freq→有多少字符达到该频率?
- 如果只要求频次最多相差1,判定条件如何改变?
