给你一个字符串 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
第一种思路:
题意等价于在 s 里找一个滑动窗口,使得此滑动窗口里的【出现次数最多字符的出现次数】>= 窗口长度 - k。
所以解答是标准滑动窗口三步法:
1. 让右指针向右一步,新字符入队
2. 调整左指针,去掉不必要或者不合适的部分。这道题是不合适。
3. 尝试刷新答案
时间复杂度: O(M + N)
空间复杂度:O(1)
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
res = left = 0
char2freq = defaultdict(int)
for right in range(0, len(s)):
char2freq[s[right]] += 1
# find the most freq char in the window
max_freq = max(char2freq.values())
for char, freq in char2freq.items():
if freq == max_freq:
max_freq_char = char
while left < right and right - left + 1 - max_freq > k:
char2freq[s[left]] -= 1
left += 1
res = max(res, right - left + 1)
return res
第二种思路:
其实我们完全不用每次循环都重新找窗口里出现次数最多的字符的出现次数,只需追踪历史窗口里出现次数最多的字符的出现次数。这一步比较难理解。
原理大概是:定义 窗口里出现次数最多的字符的出现次数 = p。对于当前窗口,假设就算 p 已经是历史上最大值,我仍然需要改大于 k 的字符来让窗口满足条件。那么在正常的情况下,我需要改的字符必然也大于k。
这里 k = 窗口长度 - p。
时间复杂度: O(M + N),数量级上不会有变化,但是确实有少量优化
空间复杂度:O(1)
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
# find a window so that the max freq for chars in that window
#. is as large as possible
left = res = max_freq = 0
char2freq = defaultdict(int)
for right, char in enumerate(s):
# enqueue right side
char2freq[char] += 1
# adjust left side
max_freq = max(max_freq, char2freq[char])
if right - left + 1 - max_freq > k:
char2freq[s[left]] -= 1
left += 1
# try to refresh answer
res = max(res, right - left + 1)
return res