【Leetcode hot 100】763.划分字母区间
问题链接
763.划分字母区间
问题描述
给你一个字符串 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 <= 500
s
仅由小写英文字母组成
问题解答
解题思路
利用贪心算法结合哈希表(或数组) 记录字符最后出现位置,通过双指针确定每个片段的边界,具体步骤如下:
- 记录字符最后位置:首先遍历字符串,用数组(因字符仅为小写字母,数组效率高于哈希表)存储每个字符在字符串中最后一次出现的下标。
- 双指针划分子段:
start
:当前片段的起始下标,初始为 0。end
:当前片段的最远边界,初始为 0。- 再次遍历字符串,对于每个字符,更新
end
为当前字符最后位置与当前end
的最大值(确保当前片段包含该字符的所有出现)。 - 当遍历到的下标
i
等于end
时,说明当前片段已包含所有必要字符,计算片段长度(end - start + 1
)并加入结果列表,同时更新start
为end + 1
,开启下一个片段。
代码实现
import java.util.ArrayList;
import java.util.List;class Solution {public List<Integer> partitionLabels(String s) {// 存储每个小写字母最后出现的下标(a-z对应索引0-25)int[] lastOccur = new int[26];int n = s.length();// 第一步:遍历字符串,记录每个字符的最后出现位置for (int i = 0; i < n; i++) {char c = s.charAt(i);lastOccur[c - 'a'] = i; // 字符c对应的索引为c-'a',更新其最后位置为i}List<Integer> result = new ArrayList<>();int start = 0; // 当前片段的起始下标int end = 0; // 当前片段的最远边界// 第二步:遍历字符串,确定每个片段的边界for (int i = 0; i < n; i++) {char c = s.charAt(i);// 更新当前片段的最远边界(必须包含当前字符的所有出现)end = Math.max(end, lastOccur[c - 'a']);// 当i达到end时,当前片段已完整(所有字符的所有出现都在[start, end]内)if (i == end) {result.add(end - start + 1); // 加入当前片段长度start = end + 1; // 更新下一个片段的起始位置}}return result;}
}
代码解释
-
字符最后位置存储:
- 利用大小为 26 的数组
lastOccur
存储每个小写字母的最后出现下标,因为小写字母仅有 26 种,数组访问效率为 O(1),优于 HashMap。 - 遍历字符串时,对于每个字符
c
,通过c - 'a'
计算其在数组中的索引,并用当前下标i
更新该位置的值(确保最终存储的是“最后一次出现”的下标)。
- 利用大小为 26 的数组
-
双指针边界确定:
start
始终指向当前未划分片段的起始位置,初始为 0。end
是当前片段的“动态最远边界”:每遇到一个字符,都要确认该字符的最后出现位置是否超出当前end
,若超出则更新end
(保证当前片段包含该字符的所有出现)。- 当
i == end
时,说明从start
到end
的所有字符,其最后出现位置都不超过end
,即该片段是“最小且完整”的,可加入结果列表,并开启下一个片段。
复杂度分析
- 时间复杂度:O(n),其中 n 是字符串长度。仅需遍历字符串两次(第一次记录最后位置,第二次划分子段),每次遍历均为线性时间。
- 空间复杂度:O(1),仅使用大小为 26 的固定数组(与字符串长度无关)和结果列表(结果列表存储的是片段长度,属于输出空间,不计入算法额外空间)。