150NeetCode 150

128.Partition Labels

MediumGreedy Ā· 7/8

Problem

You are given a string s. Split it into as many parts as possible such that each letter appears in at most one part. Concatenating the parts in order must give back s.

Return a list of the sizes of these parts.

Examples

Input:  s = "ababcbacadefegdehijhklij"
Output: [9,7,8]
Explanation: "ababcbaca", "defegde", "hijhklij". No letter appears in two parts.
             "ababcbacadefegde" would also be valid but gives fewer parts.
Input:  s = "eccbbbbdec"
Output: [10]
Explanation: 'e' appears at index 0 and 8 and 'c' at 9, so everything must stay together.
Input:  s = "abc"
Output: [1,1,1]

Constraints

  • 1 <= s.length <= 500
  • s consists of lowercase English letters.

šŸ’” Key Insight

Once a part contains a letter, it must extend at least to that letter's last occurrence. So scan left to right while keeping end = max(last[c]) over the letters seen in the current part. When the index i reaches end, nothing inside the part appears later — cut there. Cutting as early as possible is always safe, so this greedy produces the maximum number of parts.

Brute Force Expansion

TimeO(n²)SpaceO(1)

Intuition

Start a part at start. Its end must be at least the last occurrence of s[start]. But every letter between start and that end may push the end further, so keep scanning, and for each character search the rest of the string for its last occurrence. When the scan reaches the current end, close the part.

Algorithm

  1. start = 0.
  2. While start < n: end = lastIndexOf(s[start]); for i from start while i <= end, set end = max(end, s.lastIndexOf(s[i])).
  3. Record end - start + 1, set start = end + 1.

Visualization

s = "eccbbbbdec"
start=0, 'e' last at 8 -> end=8
i=1 'c' last at 9 -> end=9
... i reaches 9 -> part size 10

Code

java
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<Integer> partitionLabels(String s) {
        List<Integer> sizes = new ArrayList<>();
        int start = 0;
        while (start < s.length()) {
            int end = s.lastIndexOf(s.charAt(start));
            for (int i = start; i <= end; i++) {
                // O(n) search for each character's last position
                end = Math.max(end, s.lastIndexOf(s.charAt(i)));
            }
            sizes.add(end - start + 1);
            start = end + 1;
        }
        return sizes;
    }
}

Complexity

  • Time: O(n²) — lastIndexOf is O(n) and is called for every character.
  • Space: O(1) — excluding the output.

Complexity Comparison

Approach Time Space
Brute Force Expansion O(n²) O(1)
Merge Letter Intervals O(n) O(1)
Greedy with Last Occurrence O(n) O(1)

Common Pitfalls

  • Using only the last occurrence of the first letter of a part — letters inside the part can push the end further.
  • Returning the parts themselves instead of their sizes.
  • Forgetting to reset start = i + 1 after each cut, which makes every size cumulative.
  • Using a HashMap for last positions works but is unnecessary; an int[26] is faster and simpler.