[路飞]_程序员必刷力扣题: 763. 划分字母区间

143 阅读2分钟

「这是我参与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
};