1、题目描述
2、分析
要求:
(1)让字符串划分尽可能多的片段;
(2)同一个字母只能在一个片段中出现,不能出现在多个片段中。
思路如下:
(1)从前往后每出现一个字符,就找到这个字符最后一次出现的位置;显然,该片段的右边界至少要到此字符最后一次出现的位置。
(2)接下来要做的事情就是遍历区间内的所有字符,利用每个新字符在最右侧出现的位置去扩宽该片段的右边界。
(3)什么时候记录结果呢?
①遍历到了end此时可以收集一波结果,并更新下一片段的beg。
②另外一个容易遗漏,即如果end已经是s的最后一个元素此时我们也可以直接收集结果。
(4)是否可以优化性能?
①显然我们对所有区间的字符都去调用find_last_of是不明智的,因为很多字符会重复出现;此时我们搞个map记录下即可。
②另外,只对新出现的字符去尝试更新本片段的右边界即可,没有出现过的字符没必要继续用起最后出现的位置更新右边界了。
(5)其实我们还可以继续做优化。我们可以通过遍历一遍字符串就得到每个字符最后出现的位置,记录在数组里面即可(总共26个字母)。
(6)以"ababcbacadefegdehijhklij"为例。
我们先以int hash[27]记录每个字母最右侧出现的位置。退出条件和前面分析的一样,遍历过程就是不断扩宽end位置的过程。
①当我们遍历到第一个字符a的时候该片段右边界至少要到小标8,好在0~8中出现的字符并没有谁最后一次出现的位置超过8,所以第一个片段就是[0,8];
②继续遍历下面的d,此时该片段的右边界至少到下标14;但是在遍历到[9,14]中的小标10的e时候,就不得不把右边界扩展到包含下标为15的e。以此类推。
3、实现代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
using namespace std;
//能跑过,但是性能仅击败5%
/*
class Solution {
public:
vector<int> partitionLabels(string s) {
vector<int> res;
map<char,int> char_pos_map;
int beg = 0, end = 0;
for(int i = 0; i < s.size(); i++){
end = max(end, (int)s.find_last_of(s[i]));
if(end == s.size() || i == end ){
res.push_back(end - beg + 1);
beg = i + 1;
}
}
return res;
}
};
*/
//击败 11%了,看起来还可以继续优化。
/*
class Solution {
public:
vector<int> partitionLabels(string s) {
vector<int> res;
map<char,int> char_pos_map;
int beg = 0, end = 0;
for(int i = 0; i < s.size(); i++){
//对于每个字符我们都尝试从map中获取其右边界位置,如果没有我们就执行find_last_of更新进去。
if(char_pos_map.find(s[i]) == char_pos_map.end()){
char_pos_map[s[i]] = s.find_last_of(s[i]);
}
end = max(end, char_pos_map[s[i]]);
if(end == s.size() || i == end ){
res.push_back(end - beg + 1);
beg = i + 1;
}
}
return res;
}
};
*/
//这个就击败50%了。
/*
class Solution {
public:
vector<int> partitionLabels(string s) {
vector<int> res;
map<char,int> char_pos_map;
int beg = 0, end = 0;
for(int i = 0; i < s.size(); i++){
//对于每个字符我们都尝试从map中获取其右边界位置,如果没有我们就执行find_last_of更新进去。
if(char_pos_map.find(s[i]) == char_pos_map.end()){
char_pos_map[s[i]] = s.find_last_of(s[i]);
end = max(end, char_pos_map[s[i]]); //只有出现新字符的时候我们才尝试更新右边界,已经出现的那些字符再去更新没有意义。
}
if(end == s.size() || i == end ){
res.push_back(end - beg + 1);
beg = i + 1;
}
}
return res;
}
};
*/
//性能最高
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> res;
int beg = 0, end = 0;
for(int i = 0; i < s.size(); i++){
end = max(end, hash[s[i]-'a']);
if(end == s.size() || i == end ){
//if( i == end){
res.push_back(end - beg + 1);
beg = i + 1;
}
}
return res;
}
};
int main()
{
Solution s1;
string str = "ababcbacadefegdehijhklij";
//string str = "eccbbbbdec";
vector<int> res = s1.partitionLabels(str);
for(int len: res){
cout << len << ",";
}
cout << endl;
}