代码随想录算法训练营第36天 | 435.无重叠区间, 763.划分字母区间, 56.合并区间

435.无重叠区间

代码随想录链接

题目

LeetCode-435.无重叠区间

给定区间的集合, 返回使得剩余区间不重叠而需要移除的最少区间数量

自己的思路

和昨天气球那题有点像, 气球的题目事实上求的是有几个能聚合在一起的重叠区间, 所以其实将返回值修改为区间总数减去聚合在一起的重叠区间的个数即可.

或者也可以先把区间按起始位置从小到大排列, 然后记录重叠的区间的个数.

题解

气球那题改的解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[0] < b[0];
}
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() < 2) return 0;
sort(intervals.begin(), intervals.end(), cmp); // 按起始位置排序
int count = 1; // 记录聚合的个数
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] < intervals[i - 1][1]) { // 如果有重合, 就把重合的部分聚在一起(右区间聚拢)
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);
} else { // 否则就新增聚合区间的个数
count++;
}
}
return intervals.size() - count; // 区间的个数减去聚合区间的个数即为需要去除的区间数
}
};

记录重叠区间的个数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[0] < b[0];
}
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() < 2) return 0;
sort(intervals.begin(), intervals.end(), cmp);
int count = 0;
int end = intervals[0][1]; // 取第一个区间的右侧为分割点
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] < end) { // 如果有重叠, 更新分割点
end = min(end, intervals[i][1]);
count++;
} else { // 没重叠就将分割点置为当前点的右侧
end = intervals[i][1];
}
}
return count;
}
};

763.划分字母区间

代码随想录链接

题目

LeetCode-763.划分字母区间

给定一个字符串, 将这个字符串划分为尽可能多的片段, 同一个字母最多出现在一个片段当中, 划分的结果按顺序连接起来依然是原字符串, 返回划分的片段的长度.

自己的思路

自己的想法是先将每个字母出现的起始和结束位置记录下来, 转化为合并重叠区间的问题. 也写出来了一个代码, 但是好像和题目的思想可能表述的不是很明白, 如果把只出现了一次的字母单独分段, 在一些情况下是对的, 一些情况下是错的, 所以还是把自己写的贴出来吧.

题解

自己写的(没有AC):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[0] < b[0];
}
public:
vector<int> partitionLabels(string s) {
vector<int> result;
vector<vector<int>> intervals(26, vector<int>(2, -1));
for (int i = 0; i < s.size(); i++) { // 统计26个字母起始区间
int pos = s[i] - 'a';
if (intervals[pos][0] == -1)
intervals[pos][0] = i;
intervals[pos][1] = i;
}
for (int i = 0; i < intervals.size(); i++) { // 删除没有出现的字母的数据
if (intervals[i][0] == -1)
intervals.erase(intervals.begin() + i--);
}
sort(intervals.begin(), intervals.end(), cmp);
for (int i = 0; i < intervals.size(); i++) { // 合并只出现了一次的字母到其他项
if (intervals[i][0] == intervals[i][1]) {
if (i == intervals.size() - 1) intervals[i - 1][1] = intervals[i][0];
else intervals[i + 1][0] = intervals[i][1];
intervals.erase(intervals.begin() + i--);
}
}
sort(intervals.begin(), intervals.end(), cmp);
int start = 0, end = intervals[0][1];
for (int i = 1; i < intervals.size(); i++) { // 合并区间
if (intervals[i][0] < intervals[i - 1][1]) end = max(intervals[i][1], end); // 有重叠时, 就把区间右侧进行扩展
else { // 没重叠时就记录分段数据
result.push_back(end - start + 1);
start = intervals[i][0];
end = intervals[i][1];
}
}
result.push_back(end - start + 1);
return result;
}
};

卡哥的方法, 先统计每个字符出现的最后区间, 再重新遍历, 如果遇到了之前遍历过的所有字母的最远边界, 说明当前位置就是分割点.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
static bool cmp(const vector<int> &a, const vector<int> &b) {
return a[0] < b[0];
}
public:
vector<int> partitionLabels(string s) {
int lastPos[26] = {0};
for (int i = 0; i < s.size(); i++) // 记录所有字母的最后出现位置
lastPos[s[i] - 'a'] = i;
vector<int> result;
int left = 0, right = 0;
for (int i = 0; i < s.size(); i++) {
right = max(right, lastPos[s[i] - 'a']);
if (i == right) { // 如果当前位置就是前面所有字母的最远边界, 就进行分割
result.push_back(right - left + 1);
left = i + 1;
}
}
return result;
}
};

56.合并区间

代码随想录链接

题目

LeetCode-56.合并区间

对给定的区间中出现的重叠的区间进行合并, 区间是左闭右闭区间.

自己的思路

卡哥说这道题在今天的题中比较难, 我倒觉得还算简单的, 一次就A出来了.

同样还是先左边界排序, 右边界再判断是否重合及合并.

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
static bool cmp(const vector<int> &a, const vector<int> &b) {
if (a[0] == b[0]) return a[1] < b[1];
return a[0] < b[0];
}
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
vector<vector<int>> result;
vector<int> range; // 标记当前在合并的区间的左右边界
range.push_back(intervals[0][0]);
range.push_back(intervals[0][1]);
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] <= range[1]) range[1] = max(range[1], intervals[i][1]); // 如果当前遍历的区间与在合并的区间有重合部分, 就更新在合并的区间的右边界
else { // 不重合的话就记录前面的区间, 开始下一个区间的合并
result.push_back(range);
range[0] = intervals[i][0];
range[1] = intervals[i][1];
}
}
result.push_back(range);
return result;
}
};

代码随想录算法训练营第36天 | 435.无重叠区间, 763.划分字母区间, 56.合并区间

http://wwg.xyz/algorithm-train-day36/

作者

Giles

发布于

2023-03-22

更新于

2023-03-22

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×