字符串 S
由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。
示例 1:
输入: S = "ababcbacadefegdehijhklij" 输出: [9,7,8] 解释: 划分结果为 "ababcbaca", "defegde", "hijhklij"。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
注意:
S
的长度在[1, 500]
之间。S
只包含小写字母'a'
到'z'
。
思路一:由于要尽可能多的划分区间且每个字母最多只能出现在一个区间上,所以如果这个区间内的所有字符是字符串中对应字母唯一出现的区间,那么这个区间就是可以划分的一个区间,具体的意思是这个区间只出现这几种字符且在划分的其他区间上不会出现其他字符,所以我们用长度为26的数组memo存储26个字母出现的次数,用同样长度为26的标记数组marked表示某一个字符出现的次数是否等于0,用集合col表示遍历到当前下标集合中还剩哪些字符,我们遍历一次数组,首先把对应字母出现的次数-1,如果出现的次数为0就把对应的集合中对应的字符删去,如果集合为空,就把当前下标到左标记的长度加到res结果中。
参考代码:
class Solution {
public:
vector<int> partitionLabels(string S) {
vector<int> result;
vector<bool> marked(26, false);
vector<int> memo(26, 0);
set<int> col;
for (int i = 0; i < S.size(); i++) memo[S[i] - 'a']++;
int last = 0;
for (int i = 0; i < S.size(); i++) {
col.insert(S[i] - 'a');
memo[S[i] - 'a']--;
if (memo[S[i] - 'a'] == 0) {
if (col.count(S[i] - 'a')) col.erase(S[i] - 'a');
if (col.empty()) {
result.push_back(i-last+1);
last = i + 1;
}
}
}
return result;
}
};
思路二:发现solution中用贪心的算法可以写的更简洁,遍历一次数组,先把每个字符在最右边出现的坐标记下,由于在每个区间字符出现的左右边界一定小于等于这个区间的左右边界,那么对数组进行一次遍历,如果当前字符出现的右边界大于前面所有字符出现的右边界,则更新右边界,如果遍历到当前的下标等于右边界,则把左边界到右边界的长度加到res中,更新左边界为当前下标+1。
参考代码:
class Solution {
public:
vector<int> partitionLabels(string S) {
vector<int> res;
vector<int> marked(26, 0);
for (int i = 0; i < S.size(); i++) marked[S[i] - 'a'] = i;
int anchor = 0, j = 0;
for (int i = 0; i < S.size(); i++) {
j = max(j, marked[S[i] - 'a']);
if (i == j) {
res.push_back(i - anchor + 1);
anchor = j + 1;
}
}
return res;
}
};