题目链接:https://leetcode.cn/problems/maximum-number-of-non-overlapping-substrings/
题目大意:给一个由小写英文字母组成的字符串s
,求满足以下条件的子串的vector
,要求元素最多,如果答案的元素个数相同,取长度总和最小的那个。
- 子串之间不重叠
- 若一个子串
substr
包含某个字符c
,则所有的字符c
都必须包含在这个substr
中
思路:首先,每种字符的首个和末个的位置是很重要的,我们用map<char, <pair<int, int>> startend
来记录对应字符的起始和终止位置。
然而,刚才得到的这个range并不是符合题目条件的最大range,因为在某个字符c1
的initial range[l1, r1]
中,其它的字符也有自己的range,我们需要讲他们做好合并。
【注意】这中间其实有一个问题,比如也许字母a
因为字母b
范围扩大了,但也有可能字母b
的范围也会因为字母a
扩大,于是第一次对a
的扩大实际上是不充分的。为了避免这种情况,我们从a
到z
遍历一遍后,再从z
到a
遍历一遍确保最大范围正确。
for (char c = 'a'; c <= 'z'; c++) {
if (startend.find(c) != startend.end()) {
int l = startend[c].first, r = startend[c].second;
int l_min = l, r_max = r;
for (int idx = l; idx <= r; idx++) {
if (s[idx] != c) {
int l2 = startend[s[idx]].first, r2 = startend[s[idx]].second;
l_min = min(l_min, l2);
r_max = max(r_max, r2);
}
}
startend[c].first = l_min;
startend[c].second = r_max;
}
}
for (char c = 'z'; c >= 'a'; c--) {
if (startend.find(c) != startend.end()) {
int l = startend[c].first, r = startend[c].second;
int l_min = l, r_max = r;
for (int idx = l; idx <= r; idx++) {
if (s[idx] != c) {
int l2 = startend[s[idx]].first, r2 = startend[s[idx]].second;
l_min = min(l_min, l2);
r_max = max(r_max, r2);
}
}
startend[c].first = l_min;
startend[c].second = r_max;
}
}
于是,问题变成了有一系列的range(看成线段),求【不重叠的】【数量最多的】【总和长最小的】线段组合。那么对这些线段排序,以右端点小和长度小为排序依据,利用贪心算法将不重叠的线段一一加入即可。
完整代码
bool cmp(pair<int, int> p1, pair<int, int> p2) {
if (p1.second != p2.second)
return p1.second < p2.second;
else
return (p1.second - p1.first) < (p2.second - p2.first);
}
class Solution {
public:
map<char, pair<int, int>> startend;
vector<bool> known;
bool isKnown(int l, int r) {
for (int i = l; i <= r; i++) {
if (known[i])
return true;
}
return false;
}
vector<string> maxNumOfSubstrings(string s) {
for (int i = 0; i < s.length(); i++) {
if (startend.find(s[i]) == startend.end())
startend[s[i]].first = startend[s[i]].second = i;
else
startend[s[i]].second = i;
}
for (char c = 'a'; c <= 'z'; c++) {
if (startend.find(c) != startend.end()) {
int l = startend[c].first, r = startend[c].second;
int l_min = l, r_max = r;
for (int idx = l; idx <= r; idx++) {
if (s[idx] != c) {
int l2 = startend[s[idx]].first, r2 = startend[s[idx]].second;
l_min = min(l_min, l2);
r_max = max(r_max, r2);
}
}
startend[c].first = l_min;
startend[c].second = r_max;
}
}
for (char c = 'z'; c >= 'a'; c--) {
if (startend.find(c) != startend.end()) {
int l = startend[c].first, r = startend[c].second;
int l_min = l, r_max = r;
for (int idx = l; idx <= r; idx++) {
if (s[idx] != c) {
int l2 = startend[s[idx]].first, r2 = startend[s[idx]].second;
l_min = min(l_min, l2);
r_max = max(r_max, r2);
}
}
startend[c].first = l_min;
startend[c].second = r_max;
}
}
vector<pair<int, int>> lines;
lines.push_back(make_pair(0, s.length()-1));
for (auto it = startend.begin(); it != startend.end(); it++)
lines.push_back(it->second);
sort(lines.begin(), lines.end(), cmp);
known.resize(s.length(), false);
vector<string> valid;
for (int i = 0; i < lines.size(); i++) {
int l = lines[i].first, r = lines[i].second;
if (!isKnown(l, r)) {
valid.push_back(s.substr(l, r-l+1));
for (int j = l; j <= r; j++)
known[j] = true;
}
}
return valid;
}
};