题目
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
提示:
S的长度在[1, 500]之间。
S只包含小写字母 'a' 到 'z' 。
思路
连着做了两道区间贪心,写这个也想直接套区间贪心的套路做,做得巨复杂。实在写不出来,换了思路才 AC。具体步骤:
- 首先统计所有字母最后出现的位置,用数组总结
- 从头开始遍历字符串,记当前字母是
c
c
c,
c
c
c 最后出现的位置是
c
e
n
d
c_{end}
cend,当前位置是
i
i
i,
i
i
i 要遍历到
r
i
g
h
t
right
right 为止,每当
c
e
n
d
>
r
i
g
h
t
c_{end}>right
cend>right 的时候,令
r
i
g
h
t
=
c
e
n
d
right = c_{end}
right=cend。遍历结束之后,
r
i
g
h
t
right
right 就是找到的子字符串的右边界,将其长度计入
ans
数组 - 重复步骤 2,直到字符串遍历结束
看了官方题解之后又优化了一下代码,总结一下:
- 26 个字母的信息统计用数组就可以,不用 map
- 只需要记录每个字母最后出现的位置即可,第一次出现的位置不重要
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
C++ 代码
class Solution {
public:
vector<int> partitionLabels(string S) {
int sizeS = S.size();
vector<int> ans, ends(26, 0);
// 记录所有字母最后出现的位置
for (int i = 0; i != sizeS; ++i)
ends[S[i] - 'a'] = i;
// 遍历字符串,记录答案
int end = 0, lastRight = -1;
for (int i = 0; i < sizeS; ++i) {
int left = i, right = ends[S[i] - 'a'];
for (int j = left; j <= right; ++j) {
if (ends[S[j] - 'a'] > right)
right = ends[S[j] - 'a'];
}
i = right;
ans.emplace_back(right - lastRight);
lastRight = right;
}
return ans;
}
};