1. 问题背景与需求分析
今天想和大家分享一道LeetCode上非常经典的字符串处理题目——"划分字母区间"(763. Partition Labels)。这道题在2023年频繁出现在各大科技公司的面试中,特别是亚马逊和谷歌的算法轮次。我第一次遇到这个问题时,被它简洁描述下隐藏的巧妙解法所吸引。
题目要求我们将字符串划分为尽可能多的片段,使得每个字母最多出现在一个片段中。举个具体例子,对于字符串"ababcbacadefegdehijhklij",最优划分是["ababcbaca", "defegde", "hijhklij"]。这种划分方式确保了'a'、'b'、'c'等字母都只出现在第一个片段,而不会出现在其他片段。
2. 核心解题思路解析
2.1 暴力解法的局限性
最直观的想法可能是尝试所有可能的分割方式,然后验证每种分割是否满足条件。但这种方法的时间复杂度会达到O(n^2),对于长字符串(比如长度500+)完全不可行。我在第一次尝试时就掉进了这个陷阱,当字符串长度超过100时程序就卡死了。
2.2 关键突破点:字母的最后出现位置
高效的解法基于一个关键观察:对于字符串中的每个字母,我们需要确保包含它的片段必须至少延伸到该字母最后一次出现的位置。这意味着我们需要:
- 首先遍历字符串,记录每个字母最后出现的位置
- 然后再次遍历,动态维护当前片段的结束边界
这种方法将时间复杂度降到了O(n),只需要两次线性扫描,空间复杂度是O(1)(因为字母表大小固定)。
3. 详细实现步骤
3.1 预处理阶段:建立字母最后位置索引
python复制def partitionLabels(s):
last = {c: i for i, c in enumerate(s)}
这行代码创建了一个字典,存储每个字符最后出现的索引位置。例如对于"ababcbaca",last字典会是:
3.2 动态划分过程
python复制start = end = 0
result = []
for i, c in enumerate(s):
end = max(end, last[c])
if i == end:
result.append(end - start + 1)
start = i + 1
这段代码维护了两个指针:
- start:当前片段的起始位置
- end:当前片段的动态结束边界
每次遇到一个新字符,我们就将end更新为该字符最后出现的位置。当遍历指针i到达end时,说明我们找到了一个符合条件的片段。
4. 边界情况与测试验证
4.1 常见边界情况
- 全相同字符的字符串(如"aaaaa"):应该返回整个字符串长度
- 所有字符都不同的字符串(如"abcdef"):应该返回[1,1,1,1,1,1]
- 空字符串:应该返回空列表
- 大小写混合的情况(题目通常说明是小写字母)
4.2 测试用例设计
我建议至少测试以下案例:
- "ababcbacadefegdehijhklij" → [9,7,8]
- "eccbbbbdec" → [10]
- "abcd" → [1,1,1,1]
- "aabbcc" → [2,2,2]
5. 算法复杂度与优化空间
5.1 时间复杂度分析
- 第一次遍历建立last字典:O(n)
- 第二次遍历进行划分:O(n)
- 总体:O(n)
5.2 空间复杂度
- last字典存储26个小写字母:O(1)固定空间
- 结果列表:O(k),k是片段数量,最坏情况O(n)
5.3 可能的优化方向
- 可以合并两次遍历为一次,但会牺牲代码可读性
- 对于特别长的字符串,可以考虑并行预处理
- 在内存紧张的环境下,可以用数组代替字典(ASCII码作为索引)
6. 实际应用场景延伸
这个问题看似简单,但其核心思想在多个实际场景中有重要应用:
- 基因序列分析:在DNA序列分段研究中,类似的划分技术用于识别保守区域
- 文本排版引擎:确定文本块的最佳分割点以避免单词跨页
- 分布式系统:数据分区时确保相关数据位于同一节点
7. 同类问题拓展
掌握这个问题后,可以尝试解决以下变种:
- LeetCode 56. 合并区间
- LeetCode 435. 无重叠区间
- LeetCode 452. 用最少数量的箭引爆气球
这些题目都使用了类似的区间处理思想,只是判断条件和处理逻辑有所不同。我在准备面试时发现,很多区间类题目都有相通之处,理解这个核心模式后可以举一反三。
8. 个人刷题心得
在反复练习这道题后,我总结了几个关键点:
- 遇到字符串问题,先考虑是否需要预处理(如建立字符索引)
- 双指针法是处理子串/子数组问题的利器
- 维护动态边界的思想在很多题目中都适用
- 写代码前先用小例子手动模拟,确保理解正确
这道题最精妙的地方在于它用O(n)时间解决了看似复杂的问题。我在面试中被问到这个问题时,面试官特别欣赏我能够从暴力解法出发,逐步分析优化到最优解的过程,而不仅仅是给出最终答案。
