1无重叠区间
给定一个区间的集合 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
思路:
-
按起始位置排序:
- 首先,根据区间的起始位置对它们进行排序。这种排序可以有效地通过确保按起始位置顺序处理区间来检查重叠。
-
贪心策略:
- 初始化
count
为 0,用于记录需要移除的重叠区间数量。 - 遍历排序后的区间,从第二个区间开始。
- 对于每个区间,检查其起始位置是否小于前一个区间的结束位置。这表示存在重叠。
- 如果存在重叠:
- 增加
count
。 - 更新当前区间的结束位置为其当前结束位置与前一个区间结束位置的最小值。这确保当前区间调整以消除与前一个区间的重叠。
- 增加
- 初始化
-
结果:
- 循环结束时,
count
的值即为最大可保留的不重叠区间数量。
- 循环结束时,
代码:
class Solution {
public:
// 自定义比较函数,按照区间起始位置从小到大排序
static bool cmp(const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
}
// 求解无重叠区间的最大数量
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
// 若区间数量为0,返回0
if (intervals.size() == 0) return 0;
// 按照区间起始位置排序
sort(intervals.begin(), intervals.end(), cmp);
int count = 0; // 记录重叠区间数量
// 遍历区间数组
for (int i = 1; i < intervals.size(); i++) {
// 若当前区间起始位置小于前一个区间的结束位置,说明存在重叠
if (intervals[i][0] < intervals[i - 1][1]) {
count++; // 计数加1
// 更新当前区间的结束位置为前一个区间结束位置与当前区间结束位置的最小值
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);
}
}
return count; // 返回重叠区间数量
}
};
2划分字母区间
给你一个字符串 s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s
。
返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:s = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释: 划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = "eccbbbbdec" 输出:[10]
提示:
1 <= s.length <= 500
s
仅由小写英文字母组成
思路:在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
-
统计字符出现的最后位置:
- 使用一个数组
hash
,其中hash[i]
记录字符'a' + i
最后一次出现的位置。
- 使用一个数组
-
贪心策略:
- 使用两个指针
left
和right
来表示当前区间的起始和结束位置。 - 遍历字符串 S,对于每个字符,更新
right
为当前字符的最后出现位置。 - 如果当前遍历的位置
i
等于right
,则表示当前区间可以划分,将当前区间的长度加入结果中,并更新left
到下一个字符的位置。
- 使用两个指针
-
结果:
- 遍历完成后,得到所有划分区间的长度,即为最终结果。
代码:
class Solution {
public:
vector<int> partitionLabels(string S) {
int hash[26] = {0}; // 存储每个字符的最后出现位置
for (int i = 0; i < S.size(); i++) {
hash[S[i] - 'a'] = i;
}
vector<int> result;
int left = 0;
int 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;
}
};