leetcode
leetcode 351 ~ 400
替换后的最长重复字符

替换后的最长重复字符

难度:

标签:

题目描述

给你一个字符串 s 和一个整数 k 。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。

在执行上述操作后,返回 包含相同字母的最长子字符串的长度。

 

示例 1:

输入:s = "ABAB", k = 2
输出:4
解释:用两个'A'替换为两个'B',反之亦然。

示例 2:

输入:s = "AABABBA", k = 1
输出:4
解释:
将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子串 "BBBB" 有最长重复字母, 答案为 4。
可能存在其他的方法来得到同样的结果。

 

提示:

  • 1 <= s.length <= 105
  • s 仅由大写英文字母组成
  • 0 <= k <= s.length

代码结果

运行时间: 51 ms, 内存: 16.0 MB


/*
 * Problem: Given a string s and an integer k, you can change any character in the string
 * to any other uppercase English letter up to k times. Return the length of the longest
 * substring that contains the same letter after performing the above operations.
 *
 * Approach:
 * - Use the sliding window technique to find the longest substring.
 * - Use Java Streams to manage the counting of characters.
 * - Track the maximum count of any character in the window.
 * - If the current window size minus the max count is greater than k, shrink the window.
 * - Update the maximum length whenever a valid window is found.
 */
import java.util.stream.IntStream;
 
public class Solution {
    public int characterReplacement(String s, int k) {
        int[] count = new int[26];
        int maxCount = 0, maxLength = 0;
        int left = 0;
 
        for (int right = 0; right < s.length(); right++) {
            count[s.charAt(right) - 'A']++;
            maxCount = IntStream.of(count).max().orElse(0);
 
            while (right - left + 1 - maxCount > k) {
                count[s.charAt(left) - 'A']--;
                left++;
            }
 
            maxLength = Math.max(maxLength, right - left + 1);
        }
 
        return maxLength;
    }
}

解释

方法:

这个题解采用了滑动窗口的方法。首先定义两个指针l和r表示窗口的左右边界,并使用一个哈希表count来记录窗口内各字符的出现次数。变量max_str用来记录窗口中出现次数最多的字符的数量。在遍历字符串s的过程中,不断扩大窗口(右移r),并更新count和max_str。当窗口大小减去max_str的结果大于k时,说明窗口内无法通过k次替换达到全部相同的字符,此时需要左移l并相应地调整count。遍历结束后,窗口的大小即为结果。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在算法中,为什么选择滑动窗口作为解决方案,它与问题的性质有什么关联?
滑动窗口方法适用于需要在字符串或数组中寻找满足特定条件的最长或最短子串或子数组的问题。在这个问题中,我们需要找到可以通过最多k次替换操作转换为单一字符的最长子串。滑动窗口允许我们在遍历字符串的同时动态调整子串的起止位置,有效地控制在k次替换内尽可能扩大子串长度。这种方法避免了不必要的重复计算,使得算法效率更高。
🦆
当窗口内字符最多的数量`max_str`被更新时,是否只考虑当前右指针`r`指向的字符,还是需要重新计算整个窗口中所有字符的出现频率?
在这个算法中,`max_str`的更新只考虑当前右指针`r`指向的字符。每次将字符`c`加入窗口时,我们更新这个字符的计数,并检查是否需要更新`max_str`。这是因为每次只有一个新字符加入窗口,所以我们只需判断这个新增字符的计数是否超过了之前的`max_str`。这种方法避免了每次都遍历整个窗口重新计算所有字符频率,提高了算法的效率。
🦆
如何确保在左指针`l`移动后,`max_str`(窗口中字符的最大频率)仍然是准确的,没有漏掉可能的新的最大频率?
在当前算法实现中,当左指针`l`移动,即移除窗口左侧的字符时,我们减少该字符的计数。然而,我们不立即更新`max_str`,因为这将要求重新检查整个窗口的字符频率,这可能导致效率下降。在大多数情况下,`max_str`会略微过估计真实的最大频率,但这不会影响结果的正确性,因为窗口大小减去过估计的`max_str`大于k的情况下,真实的`max_str`也一定无法满足条件。这是一种以空间换时间的策略,保持算法的高效性。
🦆
算法中提到当`r - l + 1 - max_str > k`时,左指针需要移动。这个条件为何能确保窗口内的字符通过最多`k`次替换可以全部相同?
这个条件基于这样的逻辑:如果当前窗口长度减去窗口内出现次数最多的字符的数量大于k,那么即使使用所有k次替换,也无法将窗口内其他字符全部替换为出现最多的字符。因此,这时需要移动左指针来尝试找到一个新的、可能更小的窗口,在这个新窗口中,通过最多k次替换可以满足所有字符相同的条件。这是一种通过调整窗口大小来试图找到满足条件的最长子串的方式。

相关问题

至多包含 K 个不同字符的最长子串

至多包含 K 个不同字符的最长子串

最大连续1的个数 III

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 1 的最大个数

 

示例 1:

输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

示例 2:

输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

 

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1
  • 0 <= k <= nums.length