LeetCode-in-Java

3003. Maximize the Number of Partitions After Operations

Hard

You are given a 0-indexed string s and an integer k.

You are to perform the following partitioning operations until s is empty:

Before the operations, you are allowed to change at most one index in s to another lowercase English letter.

Return an integer denoting the maximum number of resulting partitions after the operations by optimally choosing at most one index to change.

Example 1:

Input: s = “accca”, k = 2

Output: 3

Explanation: In this example, to maximize the number of resulting partitions, s[2] can be changed to ‘b’. s becomes “acbca”. The operations can now be performed as follows until s becomes empty:

Hence, the answer is 3. It can be shown that it is not possible to obtain more than 3 partitions.

Example 2:

Input: s = “aabaab”, k = 3

Output: 1

Explanation: In this example, to maximize the number of resulting partitions we can leave s as it is. The operations can now be performed as follows until s becomes empty:

Hence, the answer is 1. It can be shown that it is not possible to obtain more than 1 partition.

Example 3:

Input: s = “xxyz”, k = 1

Output: 4

Explanation: In this example, to maximize the number of resulting partitions, s[1] can be changed to ‘a’. s becomes “xayz”. The operations can now be performed as follows until s becomes empty:

Hence, the answer is 4. It can be shown that it is not possible to obtain more than 4 partitions.

Constraints:

Solution

@SuppressWarnings("java:S6541")
public class Solution {
    private static final int ALPHABET_SIZE = 'z' - 'a' + 1;

    public int maxPartitionsAfterOperations(String s, int k) {
        if (k == ALPHABET_SIZE) {
            return 1;
        }
        int n = s.length();
        int[] ansr = new int[n];
        int[] usedr = new int[n];
        int used = 0;
        int cntUsed = 0;
        int ans = 1;
        for (int i = n - 1; i >= 0; --i) {
            int ch = s.charAt(i) - 'a';
            if ((used & (1 << ch)) == 0) {
                if (cntUsed == k) {
                    cntUsed = 0;
                    used = 0;
                    ans++;
                }
                used |= (1 << ch);
                cntUsed++;
            }
            ansr[i] = ans;
            usedr[i] = used;
        }
        int ansl = 0;
        ans = ansr[0];
        int l = 0;
        while (l < n) {
            used = 0;
            cntUsed = 0;
            int usedBeforeLast = 0;
            int usedTwiceBeforeLast = 0;
            int last = -1;
            int r = l;
            while (r < n) {
                int ch = s.charAt(r) - 'a';
                if ((used & (1 << ch)) == 0) {
                    if (cntUsed == k) {
                        break;
                    }
                    usedBeforeLast = used;
                    last = r;
                    used |= (1 << ch);
                    cntUsed++;
                } else if (cntUsed < k) {
                    usedTwiceBeforeLast |= (1 << ch);
                }
                r++;
            }
            if (cntUsed == k) {
                if (last - l > Integer.bitCount(usedBeforeLast)) {
                    ans = Math.max(ans, ansl + 1 + ansr[last]);
                }
                if (last + 1 < r) {
                    if (last + 2 >= n) {
                        ans = Math.max(ans, ansl + 1 + 1);
                    } else {
                        if (Integer.bitCount(usedr[last + 2]) == k) {
                            int canUse = ((1 << ALPHABET_SIZE) - 1) & ~used & ~usedr[last + 2];
                            if (canUse > 0) {
                                ans = Math.max(ans, ansl + 1 + 1 + ansr[last + 2]);
                            } else {
                                ans = Math.max(ans, ansl + 1 + ansr[last + 2]);
                            }
                            int l1 = s.charAt(last + 1) - 'a';
                            if ((usedTwiceBeforeLast & (1 << l1)) == 0) {
                                ans = Math.max(ans, ansl + 1 + ansr[last + 1]);
                            }
                        } else {
                            ans = Math.max(ans, ansl + 1 + ansr[last + 2]);
                        }
                    }
                }
            }
            l = r;
            ansl++;
        }
        return ans;
    }
}