「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」
763. 划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例1:
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
提示:
S的长度在[1, 500]之间。S只包含小写字母'a'到'z'。
转换合并区间
思路
由题目可得我们需要将字母区间尽可能多的划分成小段,并且任意一个元素只会出现在其中一段中(主要就是这个条件可能比较有迷惑性)
首先划分段,我们可以将同一字母出现的第一位置和最后位置划分成一段,如a [0,8],必然符合要求,但是要注意,这一段中间的元素可能存在于该段以外,例如,[0,8] 中间的 w的范围为[2,10] 那么我们要合并这两个区间
由此本题目转换为合并区间的题目
具体实现:
- 首先我们需要遍历整个字符串,得到每一个字母的区间并记录在indexMap中,遇到一个新字母将区间记为[i,i] ,当再次遇到该字母时更新区间下标为1的值,最后得到每个字母的起始位置和终止位置
- 声明res结果保存数组,声明curr代表正在处理的区间,初始值为s字符串的首字母的区间
因为我们是按照字符串的顺序来进行遍历的,每个元素的区间的起点默认是升序排序,因此我们,可以得到一下关系
- 遍历过程中,如果当前字母的left小于curr[1]并且right>curr[1],那么证明当前字母的区间可以合并到curr的区间中,直接更新curr[1]为当前元素的right
- 如果当前元素的left>curr[1],那么证明,当前元素以及往后的区间都不会和curr的区间发生交集了,此时将curr的长度push到res的结果中,并重置curr为当前元素的区间,继续遍历
- 循环完毕后,最后一个curr必然无法push进去,需要手动push当前curr的长度到res
最后返回res即可
var partitionLabels = function (s) {
var indexMap = {};
for (var i = 0; i < s.length; i++) {
var item = s[i];
if (indexMap[item] === undefined) {
indexMap[item] = [i, i];
} else {
indexMap[item][1] = i;
}
}
var res = [];
var curr = indexMap[s[0]];
for (var i = 1; i < s.length; i++) {
var item = s[i];
var indexInfo = indexMap[item];
var left = indexInfo[0];
var right = indexInfo[indexInfo.length - 1];
// 区间合并
if (left <= curr[1] && right > curr[1]) {
curr[1] = right
} else if (left > curr[1]) {
res.push(curr[1] - curr[0] + 1)
curr = [left, right]
}
}
res.push(curr[1] - curr[0] + 1)
return res
};