题目大意
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
注意:
字符串长度 和 k 不会超过 104。
示例 1:
输入:
s = "ABAB", k = 2
输出:
4
解释:
用两个'A'替换为两个'B',反之亦然。
示例 2:
输入:
s = "AABABBA", k = 1
输出:
4
解释:
将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子串 "BBBB" 有最长重复字母, 答案为 4。
解题思路
我们的目标是计算以位置i字符结尾时,当前的最长重复字符(重复字符不一定是s[i],只是说变换k次后,最长重复字符串以i位置为结尾)。遍历s,得到以每个位置为结尾时的最长重复字符,取最大值即可。
这道题采用滑动窗口方法来做。
当前窗口左端点为left,右端点为right,计算窗口中出现次数最多的字符数量tmp,窗口长度为l=right-left+1,如果l-tmp>k,表示在窗口中替换k次后,仍然不能统一窗口中的字符,因此窗口需要缩小,left+1。反之,则以right位置为结尾的最长重复字符就是窗口的长度。
计算完right位置后,right+1,计算下一个位置。
注意:代码中窗口左端点收缩后,并没有重新计算当前窗口中的字符出现频次最大值(也不需要重新计算):当给定curMaxCount和k后,隐含了一个条件,即最大结果就是curMaxCount+k(某个字符出现了curMaxCount次,将其它k个字符换成该字符,即最长重复)。
所以,右端点扩展后,如果窗口中右端点right的数量没有大于当前curMaxCount,则说明以right位置为结尾的最长重复字符结果不会大于上一轮的结果。
class Solution {
public:
int characterReplacement(string s, int k) {
// 能够将s中所有字符统一
if (s.size() - 1 <= k)
return s.size();
// s中全部是大写字母,因此用26长度的数组记录窗口元素的出现频次
vector<int> nums(26, 0);
// 分别是:左端点、右端点、最终结果、当前窗口出现频次的最大值
int left = 0, right = 0, res = 0, curMaxCount = 0;
for (; right < s.size(); ++right){
// right位置元素进入窗口,数组中记录信息
++nums[s[right] - 'A'];
// 相较于上一次的窗口,仅多了一个right位置元素
// 因此当前窗口最大值只需要跟s[right]的数量比较一下就行
curMaxCount = max(curMaxCount, nums[s[right] - 'A']);
// 替换K次后,仍然不能统一窗口元素,则窗口收缩(left+1)
while (right - left + 1 - curMaxCount > k)
// 窗口元素出去一个,对应的数组中-1
--nums[s[left++] - 'A'];
res = max(res, right - left + 1);
}
return res;
}
};