Medium
题目描述
老师正在准备一场有 n 道判断题的考试,其中 ‘T’ 表示正确,‘F’ 表示错误。老师想通过最大化连续相同答案的题目数量来困惑学生(连续的多个 T 或连续的多个 F)。
给你一个字符串 answerKey,其中 answerKey[i] 是第 i 个问题的原始答案。此外,还给你一个整数 k,表示你最多可以执行以下操作的次数:
- 将任意问题的答案改为 ‘T’ 或 ‘F’(即将
answerKey[i]设置为 ‘T’ 或 ‘F’)。
返回在最多执行 k 次操作后,答案字符串中连续 ‘T’ 或连续 ‘F’ 的最大数量。
示例 1:
输入:answerKey = "TTFF", k = 2
输出:4
解释:我们可以将两个 'F' 都替换为 'T',使 answerKey = "TTTT"。
有四个连续的 'T'。
示例 2:
输入:answerKey = "TFFT", k = 1
输出:3
解释:我们可以将第一个 'T' 替换为 'F',使 answerKey = "FFFT"。
或者,我们可以将第二个 'T' 替换为 'F',使 answerKey = "TFFF"。
在这两种情况下,都有三个连续的 'F'。
示例 3:
输入:answerKey = "TTFTTFTT", k = 1
输出:5
解释:我们可以将第一个 'F' 替换为 'T',使 answerKey = "TTTTTFTT"。
或者,我们可以将第二个 'F' 替换为 'T',使 answerKey = "TTFTTTTT"。
在这两种情况下,都有五个连续的 'T'。
约束条件:
- n == answerKey.length
- 1 <= n <= 5 * 10⁴
- answerKey[i] 为 ‘T’ 或 ‘F’
- 1 <= k <= n
解题思路
这是一道经典的滑动窗口问题。核心思想是分别考虑将字符串变成全 ‘T’ 和全 ‘F’ 两种情况,取其中的最大值。
解题思路:
问题转化:原问题可以转化为在最多改变 k 个字符的前提下,能够得到的最长连续相同字符子串的长度。
滑动窗口策略:
- 分别考虑将窗口内所有字符变为 ‘T’ 和变为 ‘F’ 两种情况
- 对于每种情况,使用滑动窗口维护一个区间,使得区间内需要修改的字符数不超过 k
具体实现:
- 使用双指针 left 和 right 维护滑动窗口
- count 记录当前窗口内需要修改的字符数量
- 当 count 超过 k 时,缩小左边界直到 count <= k
- 不断更新最大窗口长度
优化:可以将两次滑动窗口合并为一个函数,分别以 ‘T’ 和 ‘F’ 为目标字符调用。
时间复杂度为 O(n),空间复杂度为 O(1),是最优解法。
代码实现
class Solution {
public:
int maxConsecutiveAnswers(string answerKey, int k) {
return max(helper(answerKey, k, 'T'), helper(answerKey, k, 'F'));
}
private:
int helper(string& answerKey, int k, char target) {
int left = 0, right = 0, count = 0, maxLen = 0;
while (right < answerKey.length()) {
if (answerKey[right] != target) {
count++;
}
while (count > k) {
if (answerKey[left] != target) {
count--;
}
left++;
}
maxLen = max(maxLen, right - left + 1);
right++;
}
return maxLen;
}
};
class Solution:
def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
def helper(target):
left = count = 0
max_len = 0
for right in range(len(answerKey)):
if answerKey[right] != target:
count += 1
while count > k:
if answerKey[left] != target:
count -= 1
left += 1
max_len = max(max_len, right - left + 1)
return max_len
return max(helper('T'), helper('F'))
public class Solution {
public int MaxConsecutiveAnswers(string answerKey, int k) {
return Math.Max(Helper(answerKey, k, 'T'), Helper(answerKey, k, 'F'));
}
private int Helper(string answerKey, int k, char target) {
int left = 0, count = 0, maxLen = 0;
for (int right = 0; right < answerKey.Length; right++) {
if (answerKey[right] != target) {
count++;
}
while (count > k) {
if (answerKey[left] != target) {
count--;
}
left++;
}
maxLen = Math.Max(maxLen, right - left + 1);
}
return maxLen;
}
}
var maxConsecutiveAnswers = function(answerKey, k) {
const helper = (target) => {
let left = 0, count = 0, maxLen = 0;
for (let right = 0; right < answerKey.length; right++) {
if (answerKey[right] !== target) {
count++;
}
while (count > k) {
if (answerKey[left] !== target) {
count--;
}
left++;
}
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
};
return Math.max(helper('T'), helper('F'));
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个字符最多被访问两次(右指针和左指针各一次) |
| 空间复杂度 | O(1) | 只使用常数额外空间 |