2026 年 7 月 11 日
这篇笔记讲解 LeetCode 1234「替换子串得到平衡字符串」。作者指出,待替换的连续子串内部是什么字符并不重要,因为它最终会被全部改写,关键是窗口外的部分:只要窗口外每个字符的出现频次都不超过目标频次(长度除以 4),窗口内就一定可以改成合法内容,这个判断被定义为 check()。文中用两个正例说明同一字符串可划分出不同的合法窗口,又给出一个反例:无论怎样替换窗口内字符,A 仍出现 3 次,从而验证上述规律。最后给出基于滑动窗口的 AC 代码。
题目是这样说的:替换一段连续子串,使整个字符串变成平衡 。就是说: 这段连续子串可以删掉,然后填充新的字符,让整体平衡
原字符串
窗口里面:
XXXX
↓
替换以后
YYYY
如图,可以发现一个规律,就是窗口里面的字符串是什么,是否平衡并不重要。反正最后都会变成新的。也就是说,连续子串的这一段窗口都是待修改区域。
那么下意识就想到窗口内和窗口外的区别,这也是 滑动窗口 的一个规律,窗口内能随意修改吗?窗口外是否都不能改呢?
基本按照我们思维惯性可以往下面思考:
如果窗口外已经合法,那么窗口里面一定可以改成合法
所以来看看下面的这个例子

根据题意,我们可以得到这4个字符的出现频次只能是2次, A 字符多了一次,按照滑动窗口划分左右边界。我随意划分了2种,都是合法的
举一个反例

所以可以总结一个规律:
我们不妨将这个判断定义为 check()
可以定义伪代码
if(check()) l++;
class Solution {
public int balancedString(String s) {
int[] cnt = new int[4];
char[] chs = s.toCharArray();
for(char cur : chs){
cnt[index(cur)]++;
}
int validCount = 0;
int target = chs.length / 4;
for(int cur : cnt) if(cur == target) validCount++;
if(validCount == 4) return 0;
int l = 0;
int minlen = Integer.MAX_VALUE;
for(int r = 0; r < chs.length; r++){
cnt[index(chs[r])]--;
while(check(cnt, target)){
minlen = Math.min(minlen, r - l + 1);
cnt[index(chs[l])]++;
l++;
}
}
return minlen;
}
public boolean check(int[] cnt, int target){
for(int cur : cnt){
if(cur > target) return false;
}
return true;
}
public int index(char c){
if(c == 'Q') return 0;
if(c == 'W') return 1;
if(c == 'E') return 2;
if(c == 'R') return 3;
return -1;
}
}
正在加载评论...