Partition Labels
Cut a string into the maximum number of parts such that no letter appears in more than one part.
Problem#
Partition the string into the largest number of substrings such that each letter appears in at most one substring. Return the lengths of the partitions.
Examples#
"ababcbacadefegdehijhklij"→[9, 7, 8]"eccbbbbdec"→[10]
Constraints#
1 <= s.length <= 500, lowercase.
Hints#
Hint 1
Approach#
First pass: record last[c] for each letter. Second pass: scan with end = max(end, last[s[i]]). When i == end, we’ve reached a frontier no earlier letter extends past — close the partition.
Solution#
class Solution {public: vector<int> partitionLabels(string s) { int last[26] = {0}; for (int i = 0; i < (int)s.size(); ++i) last[s[i] - 'a'] = i; vector<int> ans; int start = 0, end = 0; for (int i = 0; i < (int)s.size(); ++i) { end = max(end, last[s[i] - 'a']); if (i == end) { ans.push_back(i - start + 1); start = i + 1; } } return ans; }};func partitionLabels(s string) []int { var last [26]int for i := 0; i < len(s); i++ { last[s[i]-'a'] = i } var ans []int start, end := 0, 0 for i := 0; i < len(s); i++ { if last[s[i]-'a'] > end { end = last[s[i]-'a'] } if i == end { ans = append(ans, i-start+1) start = i + 1 } } return ans}class Solution: def partitionLabels(self, s: str) -> List[int]: last = {c: i for i, c in enumerate(s)} ans = [] start = end = 0 for i, c in enumerate(s): if last[c] > end: end = last[c] if i == end: ans.append(i - start + 1) start = i + 1 return ansfunction partitionLabels(s) { const last = new Array(26).fill(0); for (let i = 0; i < s.length; i++) { last[s.charCodeAt(i) - 97] = i; } const ans = []; let start = 0, end = 0; for (let i = 0; i < s.length; i++) { const li = last[s.charCodeAt(i) - 97]; if (li > end) end = li; if (i === end) { ans.push(i - start + 1); start = i + 1; } } return ans;}class Solution { public List<Integer> partitionLabels(String s) { int[] last = new int[26]; for (int i = 0; i < s.length(); i++) last[s.charAt(i) - 'a'] = i; List<Integer> ans = new ArrayList<>(); int start = 0, end = 0; for (int i = 0; i < s.length(); i++) { int li = last[s.charAt(i) - 'a']; if (li > end) end = li; if (i == end) { ans.add(i - start + 1); start = i + 1; } } return ans; }}function partitionLabels(s: string): number[] { const last: number[] = new Array(26).fill(0); for (let i = 0; i < s.length; i++) { last[s.charCodeAt(i) - 97] = i; } const ans: number[] = []; let start = 0, end = 0; for (let i = 0; i < s.length; i++) { const li = last[s.charCodeAt(i) - 97]; if (li > end) end = li; if (i === end) { ans.push(i - start + 1); start = i + 1; } } return ans;}Editorial#
The partition boundary is forced by the latest occurrence of any letter in the current segment. The window grows as we see letters whose last occurrence is further right. When i catches up to end, no letter we’ve seen so far extends past i, so it’s safe to cut — and cutting greedily here maximizes the partition count.
Complexity#
- Time: O(n).
- Space: O(1) (26 letters).
Concept revision#
Related#