Optimal Partition of String

Given a string s, split it into substrings so that no letter appears more than once within any one substring, using as few substrings as possible. Return that number. This is LeetCode 2405.

Example 1
Input: s = "abacaba"
Output: 4
Explanation: for example "ab", "ac", "ab", "a".
Example 2
Input: s = "ssssss"
Output: 6
Explanation: every "s" must be on its own.

How to Think About It

Step 1: Make each piece as long as possible

Read the string from the left and keep adding letters to the current piece. Only start a new piece when the next letter is already in the current one.

Step 2: Why greedy is optimal

Ending a piece early never helps: a shorter piece leaves the next piece with more letters to hold, and more letters can only cause more repeats. So the longest possible first piece is always part of some best answer, and the same argument repeats for the rest of the string.

Step 3: Track the letters with a bitmask

The current piece's letters fit in one int: bit c is set if letter c is in it. Checking is used & bit, adding is used |= bit, and starting a new piece is used = 0.

Walkthrough

s = "abacaba".

LetterAlready in piece?PiecesCurrent piece
ano1a
bno1ab
ayes: new piece2a
cno2ac
ayes: new piece3a
bno3ab
ayes: new piece4a

Java Solution

parts starts at 1 because the first letter always opens a piece.

Java
public class OptimalPartitionOfString {

    public static int partitionString(String s) {
        int parts = 1;
        int used = 0;                         // bit c is set if letter c is in the current part
        for (char ch : s.toCharArray()) {
            int bit = 1 << (ch - 'a');
            if ((used & bit) != 0) {
                parts++;                      // the letter repeats: start a new part
                used = 0;
            }
            used |= bit;
        }
        return parts;
    }

    public static void main(String[] args) {
        System.out.println(partitionString("abacaba"));   // 4
        System.out.println(partitionString("ssssss"));    // 6
    }
}

The original implementation, OptimalPartitionOfString.java, is on GitHub.

Complexity

  • Time: O(n).
  • Space: O(1): a single integer.

When the pieces must be different strings rather than have different letters, greedy no longer works: see Break a String Into Maximum Number of Unique Substrings.