class Solution:
def partitionLabels(self, s: str) -> list[int]:
last = {ch: i for i, ch in enumerate(s)}
partitions = []
start = end = 0
for i, ch in enumerate(s):
end = max(end, last[ch])
if i == end:
partitions.append(end - start + 1)
start = i + 1
return partitions
class Solution {
public:
vector<int> partitionLabels(string s) {
vector<int> last(26, 0);
for (int i = 0; i < s.size(); ++i)
last[s[i] - 'a'] = i;
vector<int> res;
int start = 0, end = 0;
for (int i = 0; i < s.size(); ++i) {
end = max(end, last[s[i] - 'a']);
if (i == end) {
res.push_back(end - start + 1);
start = i + 1;
}
}
return res;
}
};
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> res = new ArrayList<>();
int start = 0, end = 0;
for (int i = 0; i < s.length(); ++i) {
end = Math.max(end, last[s.charAt(i) - 'a']);
if (i == end) {
res.add(end - start + 1);
start = i + 1;
}
}
return res;
}
}
var partitionLabels = function(s) {
const last = {};
for (let i = 0; i < s.length; ++i)
last[s[i]] = i;
const res = [];
let start = 0, end = 0;
for (let i = 0; i < s.length; ++i) {
end = Math.max(end, last[s[i]]);
if (i === end) {
res.push(end - start + 1);
start = i + 1;
}
}
return res;
};
Given a string s
, you are to partition it into as many parts as possible so that each letter appears in at most one part. After partitioning, return a list of integers representing the size of these parts.
Key constraints:
s = "ababcbacadefegdehijhklij"
, the output should be [9,7,8]
.
At first glance, one might consider checking all possible partitions and verifying for each if the condition is met, but this would be extremely inefficient. We want to maximize the number of partitions, but we must ensure that no character appears in more than one partition.
The key insight is: for every character, all its occurrences must be contained within a single partition. So, if we know the last position of each character, we can determine the minimum range that must be included for each partition. When iterating through the string, we can expand the current partition to cover the last occurrence of each character we see. When our current index matches the farthest last occurrence seen so far, we know we can safely cut the partition here.
To solve the problem efficiently, we follow these steps:
start
(start of current partition) and end
(end of current partition).i
, update end
to be the maximum of the current end
and the last occurrence of the character at i
.i
reaches end
, it means all characters in the current partition do not appear later. Record the size (end - start + 1
) and start a new partition from i+1
.
Let's use the example s = "ababcbacadefegdehijhklij"
:
Brute-force approach: Would involve checking all possible partitions and verifying each for the unique occurrence condition, which is exponential time (very inefficient and impractical).
Optimized approach:
The Partition Labels problem is elegantly solved by tracking the last occurrence of each character and expanding partitions greedily to ensure no character crosses partition boundaries. This allows for a linear-time solution that is both efficient and easy to understand. The approach leverages simple data structures and a two-pass strategy, making it a classic example of how pre-processing and greedy methods can simplify what initially seems to be a complex partitioning task.