题目
https://leetcode-cn.com/problems/longest-repeating-character-replacement/
思路
计算每个子串的字符总数,只要减去出现最多的字符的出现次数,剩下的就是需要替换的字符。只要出现替换的字符不超过k, 就不需要移动左指针 i 。
原版代码
/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var characterReplacement = function(s, k) {
let result = 0;
let max = 0;
let cnt = new Array(26);
cnt.fill(0,0,27);//记录每个字符在子串中出现的次数
for (let i = 0, j = 0; j < s.length; j++) {
let pos = s[j].charCodeAt() - 'A'.charCodeAt();
cnt[pos]++;//对应字母出现次数+1
max = Math.max(max, cnt[pos]);//实时更新最大
//大于k的话就换不完了,移动直到满足条件
while (j - i + 1 - max > k){
max = 0;
cnt[s[i++].charCodeAt() - 'A'.charCodeAt()]--;
for (element of cnt){
max = Math.max(max, element);
}
}
result = Math.max(result, j - i + 1);
}
return result;
};
优化代码
在原版的代码里面,很大一部分代码在 i 向右移动的时候去维护max的数值。这是因为在 i 向右移动的时候,很可能会造成max的减少,如果使用Math.max(max, s[s[i].charCodeAt - 'A'.charCodeAt]) ,是找不到新的max的。因为旧的max会遮盖掉新的max的减少。
但其实,维护max是不必要的。我们的result是通过 j - i + 1 算出来的,同时,我们达到最大的result的时候,这个子串应该是由max的字符 + k 个替换好的字符组成的。
即有:j - i + 1 = max + k
k不变,所以我们找到最大的答案,其实找到max的最大值就可以了。
因此只需要在max变大的时候维护max就可以
/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var characterReplacement = function(s, k) {
let result = 0;
let cnt = new Array(26).fill(0);//cnt记录的永远都是【窗口里的字母】在【窗口中】出现的次数
let max = 0;//用于统计窗口中某字母的最大重复次数
//窗口只会不变(max不更新时)或者是增大(max更新时)
for (let i = 0, j = 0; j < s.length; j++){
let pos = s[j].charCodeAt() - 'A'.charCodeAt();
cnt[pos]++;
max = Math.max(max, cnt[pos]);
//当j跑的太远超过了k+max的容量的时候,把i往右边挪动(窗口滑动)
//这个时候为什么不更新max呢?因为是max把这个窗口撑到了这么大,就算max不再有过去那么大了,窗口的大小还在。
//直到有了新的max,把窗口继续撑大
if (j - i + 1 > k + max) {
pos = s[i].charCodeAt() - 'A'.charCodeAt();
cnt[pos]--;
i++;
}
result = Math.max(result, j - i + 1);
}
return result;
};