- ♂️ 作者:海码007
- 专栏:算法专栏
- 标题:【Hot 100】763. 划分字母区间
- ❣️ 寄语:书到用时方恨少,事非经过不知难!
这道题的思路像是“预处理+滑动窗口”,首先是预处理,因为每个字母只能出现在一个区间里面,所以肯定需要知道每个字母的最右侧边界位置。预处理就是做这个事情的。(那么有些人就会想为什么不记录左侧位置呢?因为我们是从左侧往右侧遍历的,所以不需要)。
接下来就是借助滑动窗口的思想,在遍历 字符串s 的时候,更新当前区间的最大右边界。这个右边界就是当前区间字母最后出现的位置。当前区间一定要将其包括进来。然后当遍历的位置等于最大右边界时,此时满足条件的一个区间就得到了。需要两个额外的变量来记录左右边界索引,因为计算区间的大小。
- 题目链接:
- 做题状态:
为了解决“每个字母最多只出现在一个区间中”的问题,采用的是贪心策略,目标是在遍历过程中尽早确定每一个可以独立划分的区间。整个解题思路可以分为两个主要阶段:
我们首先需要知道每个字符的最右侧位置(即最后一次出现的位置),这样在遍历时就能判断一个区间内的所有字符是否都已经包含完整。由于题目中限定字符串由小写字母组成,因此可以直接使用一个大小为 26 的整型数组 last[26]
,通过 s[i] - 'a'
将字符映射到对应数组下标。在一次遍历中,我们将每个字符的最新索引位置记录下来。
在第二轮遍历中,我们从左到右扫描字符串。使用两个变量:
start
表示当前区间的起点;end
表示当前区间中所有字符的最远右边界(可能会动态扩大)。每访问一个字符 s[i]
,就将 end
更新为 max(end, last[s[i]])
,也就是说当前区间至少要覆盖这个字符的最远位置。如果我们发现当前位置 i
正好等于 end
,说明当前区间中的所有字符都不会再在后续出现了,此时可以安全地进行一次划分,并将该区间长度 end - start + 1
添加到结果数组中。随后更新 start = end + 1
,开启下一段区间的判断。然后重新
class Solution {
public:
vector<int> partitionLabels(string s) {
int last[26]; // 记录每个字母的最后一次出现位置
int length = s.size();
// 第一步:记录每个字符的最右侧位置
for (int i = 0; i < length; ++i) {
last[s[i] - 'a'] = i;
}
vector<int> partition;
int start = 0, end = 0;
// 第二步:遍历并划分区间
for (int i = 0; i < length; ++i) {
end = max(end, last[s[i] - 'a']); // 扩大当前分段右边界
// 到达当前分段的终点,开始划分
if (i == end) {
partition.push_back(end - start + 1); // 记录长度
start = end + 1; // 开启新分段
}
}
return partition;
}
};