LeetCode题练习与总结:替换后的最长重复字符--424
一、题目描述
给你一个字符串 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 <= 10^5
s
仅由大写英文字母组成0 <= k <= s.length
二、解题思路
这个问题可以使用滑动窗口的技巧来解决。滑动窗口是一种常用的处理字符串或数组问题的方法,通过两个指针(通常称为left和right)来表示一个窗口,并随着算法的进行动态调整窗口的大小。
以下是解题步骤:
- 初始化两个指针left和right,都指向字符串的开始位置。
- 使用一个数组或者哈希表来记录当前窗口中每个字符的出现次数。
- 维护一个变量maxCount,记录当前窗口中出现次数最多的字符的出现次数。
- 移动right指针,扩大窗口,并更新字符出现次数和maxCount。
- 当窗口大小减去maxCount大于k时,说明无法通过替换k个字符来使得窗口内的所有字符相同,因此需要移动left指针,缩小窗口。
- 在每次移动right指针时,更新结果,即当前窗口的大小(right - left + 1)。
- 最终结果是所有窗口中最大的大小。
三、具体代码
class Solution {
public int characterReplacement(String s, int k) {
int left = 0, right = 0, maxCount = 0, result = 0;
int[] count = new int[26]; // 因为字符串只包含大写字母,所以使用大小为26的数组记录每个字符的出现次数
while (right < s.length()) {
// 更新当前字符的出现次数
count[s.charAt(right) - 'A']++;
// 更新当前窗口中出现次数最多的字符的出现次数
maxCount = Math.max(maxCount, count[s.charAt(right) - 'A']);
// 如果当前窗口大小减去出现次数最多的字符的出现次数大于k,则移动left指针
if (right - left + 1 - maxCount > k) {
count[s.charAt(left) - 'A']--;
left++;
}
// 更新结果
result = Math.max(result, right - left + 1);
// 移动right指针
right++;
}
return result;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 代码中有一个while循环,该循环会遍历字符串
s
中的每个字符一次。 - 在while循环内部,对于每个字符,我们进行以下操作:
- 更新字符出现次数的数组
count
,这是一个O(1)的操作,因为数组索引是常数时间访问的。 - 更新
maxCount
,这是一个O(1)的操作,因为它只是比较两个整数值。 - 检查是否需要移动
left
指针,这也是一个O(1)的操作。 - 更新结果
result
,同样是O(1)的操作。
- 更新字符出现次数的数组
- 因此,while循环的每次迭代都是O(1)的操作,而循环会执行
n
次,其中n
是字符串s
的长度。
综上所述,整个函数的时间复杂度是O(n),其中n是字符串s
的长度。
2. 空间复杂度
- 代码中使用了一个固定大小的数组
count
来记录每个字符的出现次数。由于字符串s
只包含大写英文字母,所以数组的大小是26,这是一个常数。 - 除了
count
数组外,代码中使用的其他变量(left
、right
、maxCount
、result
)都是基本数据类型,它们占用固定大小的空间,不随输入字符串s
的长度变化。
因此,整个函数的空间复杂度是O(1),表示它使用了常数级别的额外空间。
五、总结知识点
-
类定义:
class Solution
定义了一个名为Solution
的类。 -
方法定义:
public int characterReplacement(String s, int k)
定义了一个公共方法characterReplacement
,它接受一个字符串s
和一个整数k
作为参数,并返回一个整数。 -
基本数据类型:代码中使用了
int
类型的变量,用于存储索引、计数、最大计数和结果。 -
数组:
int[] count = new int[26];
创建了一个大小为26的整型数组,用于记录每个大写英文字母在当前窗口中的出现次数。 -
ASCII码:
s.charAt(right) - 'A'
使用了字符的ASCII码值来进行数组索引的计算,这里将大写字母映射到数组索引。 -
循环:
while (right < s.length())
使用了一个while
循环来遍历字符串。 -
条件判断:
if (right - left + 1 - maxCount > k)
使用了条件判断来决定是否需要移动左指针。 -
数组元素访问和更新:
count[s.charAt(right) - 'A']++
和count[s.charAt(left) - 'A']--
访问和更新数组中的元素。 -
最大值计算:
maxCount = Math.max(maxCount, count[s.charAt(right) - 'A']);
使用Math.max
方法来计算两个值中的最大值。 -
滑动窗口技术:代码使用了滑动窗口技术来找到包含相同字母的最长子字符串的长度。滑动窗口通过两个指针
left
和right
来维护当前窗口的边界。 -
算法逻辑:代码的逻辑体现了贪心算法的思想,即总是尝试将当前窗口扩展到最大,当窗口不满足条件时再收缩。
-
方法返回值:
return result;
表示方法的返回值,这里是计算得到的最长子字符串的长度。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。