替换后的最长重复字符
题目描述
给你一个字符串 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 <= 105s仅由大写英文字母组成0 <= k <= s.length
题解
因为数据只有26个字符,所以可以先枚举目标字符串的字符是什么。第二重循环枚举左右端点,维护区间 中不等于最终目标字符的数量不超过操作数 ,可以用双指针去枚举一个端点,假设枚举右端点,当右端点 确定后,考虑左端点最左能到达什么地方。
假设 是满足区间 中不等于最终目标字符的数量不超过操作数 最左的位置(也就是不等于最终目标字符的数量恰好等于操作数 ),再往左就不满足这个条件了。
我们可以发现,当 单调向右走时, 也一定随着 单调向右走,也就是 是 的一个单调函数,因此我们可以使用双指针算法。
反证法证明单调性:
定义 是最靠左并且满足条件的位置,
假设当 往右走到 , 往左走到 ,满足区间 中恰好有 个不同的元素, 那么区间 中不等于目标字符的数量一定不超过 ,因为区间更小了,此时我们可以发现 的位置与我们定义的矛盾,因为 可以再往左移动到 。与假设矛盾,所以当 往右走时, 一定不会往左走。
代码
class Solution {
public:
int characterReplacement(string s, int k) {
// 维护区间[j, i]中不等于目标字符的数量不超过操作数k
int res = 0;
// 枚举目标字符
for (char c = 'A'; c <= 'Z'; c++) {
// cnt 记录窗口内等于目标字符的数量
for (int i = 0, j = 0, cnt = 0; i < s.size(); i++) {
if (s[i] == c) cnt++;
// 如果窗口内不等于目标字符的数量大于k 则维护窗口直到满足这个条件
while (i - j + 1 - cnt > k) {
// 窗口划出一个字符 如果划出字符是目标字符 则cnt--
if (s[j] == c) cnt--;
j++;
}
res = max(res, i - j + 1);
}
}
return res;
}
};