算法训练day28 | php | 435. 无重叠区间 , 763.划分字母区间 , 56. 合并区间

一、力扣题435. 无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 

示例 1:

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

提示:

  • 1 <= intervals.length <= 105
  • intervals[i].length == 2
  • -5 * 104 <= starti < endi <= 5 * 104

        依旧是区间问题,和上一天的气球那题差不多。本题要求移除最少的区间,使剩下的 区间之间互不重叠。并且 [ 1, 2 ] 和 [ 2, 3 ] 这样边缘接触的区间不算重叠。

        首先依旧要先对区间进行排序,按区间的起点进行升序排序,相同起点的区间按终点进行升序排序。

        本题的本质依旧是求相邻区间之间的重叠区域,当然这个相邻不只是指两个区间,比如有可能连续的三个区间都存在共同的重叠区域。又因为已经将区间排序了,所以只需要获取重叠区域的右端点进行判断。

        设置一个变量 end 为重叠区域的右端点,初始值为第一个区间的右端点,即先设置第一个区间为重叠区域,先遍历到下一个区间判断其与上一个区间是否存在重叠区域,存在则删除区间数 + 1,且 end 变为新的重叠区域的右端点;不存在 end 则变为当前区间的右端点。

function eraseOverlapIntervals($intervals) {
        usort($intervals, function($a, $b) {
            if($a[0] == $b[0]) return $a[1] > $b[1];
            return $a[0] > $b[0];
        });

        $sum = 0;
        $end = $intervals[0][1];
        for($i = 1; $i < count($intervals); $i++) {
            if($end > $intervals[$i][0]) {
                $sum++;
                $end = min($intervals[$i][1], $end);
            } else {
                $end = $intervals[$i][1];
            }
        }
        return $sum;
    }

二、力扣题763. 划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。

示例 1:

输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。 

示例 2:

输入:s = "eccbbbbdec"
输出:[10]

提示:

  • 1 <= s.length <= 500
  • s 仅由小写英文字母组成

         这道题一开始根本没有思路,去看了题解才恍然还有这种解法。了解后发现这道题和之前的跳跃问题有点相像,本质上都是求当前的最远范围。

        先把字符串遍历一遍,获取字符串中出现过的字母在字符串中最后出现的位置,即最远位置。

        要把字符串划分为多个片段,且同一字母最多出现在一个片段内。所以拿到每个字母的最远位置,我们就明确了切割点。

        但还有一个问题待解决,字母之间是交叉在一起的,比如包含全部 ' a ' 的片段中还包含了 ' b ',但是 ' b '字母不止出现在这个片段中。例如:' aababb',划分为 ' aaba ' 显然是错误的。

        所以遍历字符串时,切割点是一直在变的,切割点应该是当前遍历过的所有字母中最大的最远位置,例如上面举例的 ' aababb',b 的最远位置比 a 的大,所以切割点不应该是 a 的最远位置。直到到达切割点 i 所在的第 i 个位置,才可以进行切割,然后遍历下一个字母,找到新的切割点。

function partitionLabels($s) {
        $used = [];
        for($i = 0; $i < strlen($s); $i++) {
            $used[$s[$i]] = $i;
        }
        
        $max = -1;
        $cur = 0;
        $res = [];
        for($i = 0; $i < strlen($s); $i++) {
            $max = max($used[$s[$i]], $max);
            if($max == $i) {
                $res[] = $i - $cur + 1;
                $cur = $i + 1;
                $max = -1;
            }
        }

        return $res;
    }

三、力扣题56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

  • 1 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104

         本题依旧是区间问题,但和之前的题目不一样的是,之前的题目都是通过获取重叠区域来进行比较,也就是求重叠区间之间的交集。但本题是合并重叠的区间,也就是求重叠区间的并集。

        所以变量 end 就要改变含义,从交集区域的右端点,变为并集区间的右端点。而其他逻辑和之前的区间题目差不多。

        但和上面的 '无重叠区间' 题目还有一点不同,本题定义的重叠包括边缘相触的区间,例如 [ 1, 2 ] 和 [ 2, 3 ]。

function merge($intervals) {
        usort($intervals, function($a, $b) {
            if($a[0] == $b[0]) return $a[1] > $b[1];
            return $a[0] > $b[0];
        });
        
        $start = $intervals[0][0];
        $end = $intervals[0][1];
        $res = [];
        for($i = 1; $i < count($intervals); $i++) {
            if($end >= $intervals[$i][0]) {
                $end = max($intervals[$i][1], $end);
            } else {
                $res[] = [$start, $end];
                $start = $intervals[$i][0];
                $end = $intervals[$i][1];
            }
        }
        $res[] = [$start, $end];
        return $res;
    }

你可能感兴趣的:(算法,php,leetcode)