Day31[26/3/31]T763划分字母区间
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 "ababcc" 能够被分为 ["abab", "cc"],但类似 ["aba", "bcc"] 或 ["ab", "ab", "cc"] 的划分是非法的。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = "eccbbbbdec"
输出:[10]
提示:
1 <= s.length <= 500s仅由小写英文字母组成
解题思路
主要的思路是:任何元素的第一次出现和最后一次出现的位置构成一个区间,然后将有重叠的区间合并,到最后合并不了,就是答案了。
如何用贪心思想来完成这个区间合并?
首先,遍历整个数组构建一个哈希表,存储每个元素最后一次出现的位置。
后续,你从头遍历数组的时候,就查哈希表就可以了,这可以省好多时间。
然后来个例子:
已经确定了 nums[0] 这个元素最后一次出现是在 nums[3]
那么你只需要检查 nums[1], nums[2] 这两个元素最后一次出现在哪
如果最后一次出现不超过 3,那么 [0,3] 就是一个确定的区间了
否则,假设 nums[2] 在 nums[9] 上也出现了,那么你下一次就继续检查
nums[3+1] 到 nums[9] 之间有没有最后一次出现在超过 [9] 的
重复上述操作即可。
Code
#include <iostream>
#include <vector>
#include <unordered_map>
#include <numeric>
using namespace std;
class Solution
{
public:
vector<int> partitionLabels(string s)
{
unordered_map<char, int> mp;
vector<int> result;
// 1. 建表存最后位置
for (int i = 0; i < s.size(); i++)
{
mp[s[i]] = i;
}
// 2. 一段一段找区间
int left = 0, right = mp[s[0]]; // 单次寻找的左右区间
int next_range = mp[s[0]]; // 下次寻找的右区间
while (left < s.size())
{
if (mp[s[left]] == left)
{
result.push_back(1);
left++;
right = mp[s[left]];
continue;
}
int old_left = left;
while (left <= right)
{
for (int i = left; i < right; i++)
{
if (mp[s[i]] > next_range)
{
next_range = mp[s[i]];
}
}
left = right + 1;
right = next_range;
}
result.push_back(right - old_left + 1);
right = mp[s[left]];
}
result.pop_back();
result.push_back(s.size() - accumulate(result.begin(), result.end(), 0));
return result;
}
};
auto main() -> int
{
Solution sol;
// string s = "ababcbacadefegdehijhklij";
string s = "eccbbbbdec";
// string s = "dccccbaabe";
// string s = "vhaagbqkaq";
cout << "result : " << endl;
cout << "[ ";
for (const auto &nums : sol.partitionLabels(s))
{
cout << "(" << nums << ")" << ',';
}
cout << " ]" << endl;
}