Medium
题目描述
给你一个字符串 s 和一个整数 k。你可以选择字符串中的任一字符,并将其更改为任何其他的大写英文字符。该操作最多可执行 k 次。
在执行上述操作后,返回包含相同字母的最长子字符串的长度。
示例 1:
输入:s = "ABAB", k = 2
输出:4
解释:用两个'B'替换两个'A',反之亦然。
示例 2:
输入:s = "AABABBA", k = 1
输出:4
解释:将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子字符串 "BBBB" 的最长重复字母长度为 4。
可能存在其他的方法来得到这个结果。
提示:
1 <= s.length <= 10^5s仅由大写英文字母组成0 <= k <= s.length
解题思路
解题思路
这是一道经典的滑动窗口题目。核心思想是维护一个窗口,使得窗口内除了出现频次最多的字符外,其他字符的总数不超过 k 个,这样我们就能通过最多 k 次替换操作使整个窗口变成相同字符。
算法步骤:
- 滑动窗口设计:使用双指针
left和right维护窗口边界 - 频次统计:用哈希表记录窗口内每个字符的出现次数
- 窗口有效性判断:窗口长度 - 最高频次字符数量 ≤ k
- 窗口调整:当窗口无效时,移动左指针直到窗口重新有效
关键观察:
- 我们不需要考虑具体替换哪个字符,只需要保证替换次数不超过 k
- 窗口内的最优策略是将所有非最高频字符都替换成最高频字符
- 因此判断条件是:
窗口大小 - max_count ≤ k
算法时间复杂度为 O(n),空间复杂度为 O(1)(最多26个英文字母)。
代码实现
class Solution {
public:
int characterReplacement(string s, int k) {
vector<int> count(26, 0);
int left = 0, maxCount = 0, maxLength = 0;
for (int right = 0; right < s.length(); right++) {
count[s[right] - 'A']++;
maxCount = max(maxCount, count[s[right] - 'A']);
if (right - left + 1 - maxCount > k) {
count[s[left] - 'A']--;
left++;
}
maxLength = max(maxLength, right - left + 1);
}
return maxLength;
}
};
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
count = {}
left = 0
max_count = 0
max_length = 0
for right in range(len(s)):
count[s[right]] = count.get(s[right], 0) + 1
max_count = max(max_count, count[s[right]])
if right - left + 1 - max_count > k:
count[s[left]] -= 1
left += 1
max_length = max(max_length, right - left + 1)
return max_length
public class Solution {
public int CharacterReplacement(string s, int k) {
int[] count = new int[26];
int left = 0, maxCount = 0, maxLength = 0;
for (int right = 0; right < s.Length; right++) {
count[s[right] - 'A']++;
maxCount = Math.Max(maxCount, count[s[right] - 'A']);
if (right - left + 1 - maxCount > k) {
count[s[left] - 'A']--;
left++;
}
maxLength = Math.Max(maxLength, right - left + 1);
}
return maxLength;
}
}
/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var characterReplacement = function(s, k) {
const count = {};
let left = 0, maxCount = 0, maxLength = 0;
for (let right = 0; right < s.length; right++) {
count[s[right]] = (count[s[right]] || 0) + 1;
maxCount = Math.max(maxCount, count[s[right]]);
if (right - left + 1 - maxCount > k) {
count[s[left]]--;
left++;
}
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 其中 n 是字符串长度,每个字符最多被访问两次 |
| 空间复杂度 | O(1) | 使用固定大小的数组或哈希表存储字符频次,最多26个字母 |