435.无重叠区间
给定一个区间的集合 intervals,其中 intervals[i] = [start, end]。返回 需要移除区间的最小数量,使剩余区间互不重叠。
看到本题的第一思路是排序,排序可以使重叠区域相邻,从而可以借助 for 循环遍历。问题来了,按照左区间排序,还是右区间排序?在本题中左右区间都可以,我选择左边界。排序过后,思路就和射气球问题类似,如果当前区间的左边界值小于上一个区间的右边界值,说明区这两个区间重叠,此时计数器自加。
class Solution {
private:
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[1] < b[1];
}
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() == 1) return 0;
sort(intervals.begin(), intervals.end());
int result = 0;
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] >= intervals[i - 1][1]) {
continue;
} else {
result++;
intervals[i][1] = intervals[i - 1][1];
}
}
return result;
}
};
注意,此时,还需要将当前区间的 右边界值 设置为 上一个右边界值 和 当前右边界值 中的最小值。
为什么这么做,有的同学可能会问了,计数器自加,说明当前边界已经被删除了,为什么还要取两个边界中的最小值,而不是将当前边界的右边界值拷贝为上一个边界的右边界值?
这里同学走入了一个误区,认为访问的是当前边界,所以删除的也是当前边界。其实不然,根据贪心算法的策略,全局问题为删除最少的区间,分解的局部子问题为删除重叠区间中,覆盖区间大的那个区间。因此需要取两个区间中的最小右边界值,将右边界值大的区间“删除”!!!
763.划分字母区间
给你一个字符串 s。我们要把这个字符串分为尽可能多的片段,同一字母最多出现在一个片段中。注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s。返回一个表示每个字符串片段的长度的列表。
全局问题,按照要求将给定字符串划分为尽可能多的片段;局部问题,保证每一个子串最短。如何确定子串的长度?题中要求,某一种字符只能出现在一个子串中,与重叠区间问题类似,我们可以通过求各个字符的区间长度,进而重叠字符串的的最短长度。
class Solution {
public:
vector<int> partitionLabels(string s) {
int hash[27] = {0};
for (int i = 0; i < s.size(); i++) {
hash[s[i] - 'a'] = i;
}
vector<int> result;
int left = 0, right = 0;
for (int i = 0; i < s.size(); i++) {
right = max(right, hash[s[i] - 'a']);
if (i == right) {
result.push_back(right - left + 1);
left = i + 1;
}
}
return result;
}
};
在代码中,我们通过整数数组,存储字符串中每一种字符的最远位置,并随着遍历过程不断更新最原位置,一旦 i == right,说明当前子串内的所有字符都被包括了,此时返回子串的长度,并修正 left 的位置。
56.合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [start, end]。请你合并所有重叠的区间,并返回一个 不重叠的区间数组,该数组恰好覆盖输入中的所有区间。
合并数组问题,类似射气球和无重叠无区间问题,删除数组中重叠的数组,废话不说,直接上代码。
class Solution {
private:
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[0] < b[0];
}
public:
vector<vector<int>> merge(vector<vector<int>> &intervals) {
vector<vector<int>> result; result.clear();
sort(intervals.begin(), intervals.end(), cmp);
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] > intervals[i - 1][1]) {
result.push_back(intervals[i - 1]);
} else {
intervals[i][0] = min(intervals[i][0], intervals[i - 1][0]);
intervals[i][1] = max(intervals[i][1], intervals[i - 1][1]);
}
}
result.push_back(intervals[intervals.size() - 1]);
return result;
}
};
前半部分和Carl哥的思路类似,根据数组的左边界值排序,聚集重叠区间。后半部逻辑稍有不同,在for中,我在if中判断当前区间和前一个区间是否重叠,如果不重叠则将前一个区间加入结果集中;如果重叠,则合并两个区间,扩容当前区间的范围。如此循环直至结束。此时,最后一个合并完毕的区间并没有加入结果集,因为我总是将前一个符合条件的区间加入结果集,因此在for后,将最后一个区间加入结果集。此时,最后一个区间也必定是不重叠区间。