leetcode
leetcode 2501 ~ 2550
执行操作后的最大分割数量

执行操作后的最大分割数量

难度:

标签:

题目描述

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

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

  • Choose the longest prefix of s containing at most k distinct characters.
  • Delete the prefix from s and increase the number of partitions by one. The remaining characters (if any) in s maintain their initial order.

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:
- Choose the longest prefix containing at most 2 distinct characters, "acbca".
- Delete the prefix, and s becomes "bca". The number of partitions is now 1.
- Choose the longest prefix containing at most 2 distinct characters, "bca".
- Delete the prefix, and s becomes "a". The number of partitions is now 2.
- Choose the longest prefix containing at most 2 distinct characters, "a".
- Delete the prefix, and s becomes empty. The number of partitions is now 3.
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: 
- Choose the longest prefix containing at most 3 distinct characters, "aabaab".
- Delete the prefix, and s becomes empty. The number of partitions becomes 1. 
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:
- Choose the longest prefix containing at most 1 distinct character, "xayz".
- Delete the prefix, and s becomes "ayz". The number of partitions is now 1.
- Choose the longest prefix containing at most 1 distinct character, "ayz".
- Delete the prefix, and s becomes "yz". The number of partitions is now 2.
- Choose the longest prefix containing at most 1 distinct character, "yz".
- Delete the prefix, and s becomes "z". The number of partitions is now 3.
- Choose the longest prefix containing at most 1 distinct character, "z".
- Delete the prefix, and s becomes empty. The number of partitions is now 4.
Hence, the answer is 4.
It can be shown that it is not possible to obtain more than 4 partitions.

 

Constraints:

  • 1 <= s.length <= 104
  • s consists only of lowercase English letters.
  • 1 <= k <= 26

代码结果

运行时间: 61 ms, 内存: 17.3 MB


/*
 * Problem: Given a string 's' and an integer 'k', find the maximum number of partitions
 * after optionally changing at most one character, such that each partition's prefix contains
 * at most 'k' different characters.
 * 
 * Approach (Java Streams):
 * 1. Use streams to try changing each character and calculate the partition count.
 * 2. Use streams to count the number of partitions with at most 'k' unique characters.
 */
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.Arrays;

public class Solution {
    public int maxPartitions(String s, int k) {
        return Math.max(
            // Maximum partitions without any modification
            calculatePartitions(s, k),
            // Maximum partitions with one modification
            IntStream.range(0, s.length()).flatMap(i -> IntStream.range(0, 26)
                .filter(c -> s.charAt(i) != (char) (c + 'a'))
                .mapToObj(c -> s.substring(0, i) + (char) (c + 'a') + s.substring(i + 1))
                .mapToInt(modified -> calculatePartitions(modified, k))
                .max().orElse(0)
            ).max().orElse(0)
        );
    }

    private int calculatePartitions(String s, int k) {
        int[] count = new int[26];
        int uniqueCount = 0, partitions = 0, left = 0;
        for (int right = 0; right < s.length(); right++) {
            if (count[s.charAt(right) - 'a']++ == 0) uniqueCount++;
            while (uniqueCount > k) {
                if (--count[s.charAt(left++) - 'a'] == 0) uniqueCount--;
            }
            if (uniqueCount <= k) partitions++;
        }
        return partitions;
    }
}

解释

方法:

这道题的核心是通过一次或不通过修改任何字符,来最大化字符串s的分割次数。首先,如果k等于26(字母表的长度),可以一次性删除整个字符串,所以答案为1。题解使用两个遍历过程:首先,从右向左遍历字符串,记录每个位置为起始点的段数和所包含的字符(使用位掩码表示)。然后,再从左向右遍历字符串,并结合已存储的后缀信息来决定修改字符的位置,以最大化分割数。第二遍遍历尝试通过字符替换来优化可能的分割方式,最终更新并返回最大分割数。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
如何确保修改最多一处字符就可以实现最大分割数的策略?题解中似乎没有明确说明这一点。
题解中通过两次遍历和位掩码来尝试优化分割,确保最多修改一处字符的策略主要依赖于第二次遍历时评估合并段落的可能性。通过计算当前段落和后缀段落的字符集的并集大小来决定是否可以通过修改一个字符来减少分割数。如果并集大小小于k且当前段和后缀段的字符集大小都已达到k,通过替换一个字符可能实现两个段合并。这种策略尝试在不超过修改一次的情况下,通过智能合并来达到最大分割数。
🦆
在题解中提到的位掩码是如何工作的,尤其是在处理字符集时这种表示法有什么优势?
位掩码是一种通过位运算来跟踪和操作数据的方法。在这个题解中,每个字符被映射到一个位上,例如'a'对应第0位,'b'对应第1位,依此类推。这样做的优势在于,位掩码可以高效地进行字符集的并、交和差运算,例如检查特定字符是否已存在(通过AND运算),添加新字符(通过OR运算),并且计算掩码中位为1的个数(即字符集大小)也非常高效。这种方法在处理字符集和分段操作时提供了一种紧凑且速度快的解决方案。
🦆
题解中提到的两次遍历各自的目的是什么?第一次遍历从右向左,第二次遍历从左向右,这样做的具体原因是?
题解中的第一次遍历从右向左的目的是为了建立后缀信息,即从每个位置到字符串末尾的段数和字符集。这样做可以在第二次遍历时快速获取任何位置右侧的段信息。第二次遍历从左向右,则是用来结合这些后缀信息来尝试优化分割策略,例如通过合并段落来减少分割数。这两次遍历结合起来,使得算法能在遍历过程中动态调整策略,从而实现最优分割。
🦆
题解中的函数`update`似乎是用来更新字符掩码和段数,但如果遇到已经计入掩码的字符该如何处理?
在`update`函数中,当遇到已经在当前段的字符掩码中的字符时,该字符不会引起段数的增加,也不会改变掩码的状态,因为该字符已被当前段包含。这种处理确保了每个段中的字符集合是独立且不重复的,从而正确地记录和更新段的数量。这也意味着函数只在发现新字符或必须开始新段时才更新段数和掩码,从而精确地控制分割点。

相关问题