题目描述
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 “ababcc” 能够被分为 [“abab”, “cc”],但类似 [“aba”, “bcc”] 或 [“ab”, “ab”, “cc”] 的划分是非法的。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:s = “ababcbacadefegdehijhklij”
输出:[9,7,8]
解释:
划分结果为 “ababcbaca”、“defegde”、“hijhklij” 。
每个字母最多出现在一个片段中。
像 “ababcbacadefegde”, “hijhklij” 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = “eccbbbbdec”
输出:[10]
提示:
- 1 <= s.length <= 500
- s 仅由小写英文字母组成
思考
核心是确保每个划分的片段内所有字符仅出现在该片段中,利用字符最后出现的位置确定片段边界:
- 一个片段的结束位置,必须是该片段中所有字符在整个字符串中最后出现位置的最大值(否则会有字符出现在后续片段)。
- 先预处理记录每个字符的最后出现索引,再通过一次遍历动态追踪当前片段的边界,实现线性时间划分。
算法过程
-
预处理字符最后位置:
- 遍历字符串
s,用哈希表map记录每个字符最后出现的索引(map.get(c)即为字符c在s中最后出现的位置)。
- 遍历字符串
-
动态划分片段:
- 初始化
start = 0(当前片段起始索引)、end = 0(当前片段结束索引)。 - 再次遍历字符串,对每个索引
i:- 用当前字符的最后位置更新
end(end = max(end, map.get(s[i])),确保包含当前字符的所有出现)。 - 当
i === end时,说明当前片段已包含所有该包含的字符(无字符会出现在后续),计算片段长度(end - start + 1)并加入结果,同时更新start = i + 1(下一片段起始)。
- 用当前字符的最后位置更新
- 初始化
-
返回结果:收集的片段长度数组即为答案。
时空复杂度
- 时间复杂度:O(n),
n为字符串长度。两次遍历字符串(预处理和划分),每次均为线性时间,哈希表操作是O(1)。 - 空间复杂度:O(1),哈希表存储的字符数量有限(如小写字母最多26个),空间不随
n增长。
代码
/**
* @param {string} s
* @return {number[]}
*/
var partitionLabels = function(s) {
const n = s.length;
const result = [];
const map = new Map();
for (let i = 0; i < n; i++) {
map.set(s[i], i);
}
let start = 0, end = 0;
for (let i = 0; i < n; i++) {
let endIndex = map.get(s[i]);
end = Math.max(end, endIndex);
if (i === end) {
result.push(end - start + 1);
start = i + 1;
}
}
return result;
};
2340

被折叠的 条评论
为什么被折叠?



