class Solution {
public:
vector<int> partitionLabels(string s) {
int last[26];
// 记录每个字母最后一次出现的位置
for(int i = 0; i < s.length(); i++){
last[s[i] - 'a'] = i;
}
int start = 0, end = 0;
vector<int> res;
for(int i = start; i < s.length(); i++){
// 当前字母最后出现的位置决定区间至少要延伸到哪里
end = max(end, last[s[i] - 'a']);
// i走到了当前区间的最远位置,可以进行切分
if(i == end){
// 计算当前区间的长度
res.push_back(end - start + 1);
// 下一个区间从end的下一个位置开始
start = end + 1;
}
}
return res;
}
};
总结
这道题的核心就是 "记录最后位置 + 贪心划分区间"。
1. last 数组
last[s[i] - 'a'] = i;
记录每个字母最后一次出现的位置。
例如:
s = "ababcbaca"
那么:
a 最后出现位置 = 8
b 最后出现位置 = 5
c 最后出现位置 = 7
2. end 表示当前区间最远必须到哪里
遍历当前区间时:
end = max(end, last[s[i] - 'a']);
看到一个字母,就找到它最后一次出现的位置。
如果这个位置比当前 end 更远,就把 end 向后扩展。
所以 end 可以理解成:
当前区间为了保证同一个字母不出现在其他区间,最少需要到达的位置。
3. 为什么 i == end 就能切?
当:
i == end
说明当前已经走到了区间要求的最远位置。
也就是说:
当前区间里面出现的所有字母,在后面都不会再出现了。
所以此时可以安全切分:
res.push_back(end - start + 1);
然后:
start = end + 1;
开始下一个区间。
4. 最核心的代码
实际上整道题最重要的就三步:
end = max(end, last[s[i] - 'a']);
不断扩大区间。
if(i == end)
发现可以切分。
start = end + 1;
进入下一个区间。
一句话记忆
先找每个字母最后出现的位置,然后遍历字符串,用
end不断扩大当前区间;当i走到end时,说明区间已经完整,可以切分。