问题描述
有一个小写字母组成的字符串str,字符串长度为n;字符串中某些位置可以执行修复操作:将这个位置的字符,替换为a~z中的一个字符;这个修复操作最多可以执行m次;现在想知道修复之后,字符串 str由相同字符组成的子串最大长度是多少。
测试样例
输入样例1
5 2
abcda
01110
输入样例2
7 2
abbaccb
1001001
输入样例3
3 0
aab
101
输出样例1
3
输出样例2
4
输出样例3
2
解决思路
由于字符串长度 n 最大为 2000,我们可以考虑使用滑动窗口(Sliding Window)技术来解决这个问题。滑动窗口技术可以帮助我们在 O(n) 的时间复杂度内找到满足条件的子串。
-
初始化变量:
- 使用两个指针
left和right来表示滑动窗口的左右边界。 - 使用一个变量
repairs来记录当前窗口中的修复次数。 - 使用一个变量
max_len来记录当前找到的最大子串长度。
- 使用两个指针
-
滑动窗口:
- 移动
right指针,扩大窗口,直到窗口内的修复操作次数超过m。 - 如果修复操作次数超过
m,移动left指针,缩小窗口,直到修复操作次数再次小于等于m。 - 在每次移动
right指针时,更新max_len。
- 移动
-
处理不可修改的字符:
- 如果某个位置的字符不可修改(即
str2中对应位置为0),则直接跳过该位置,不进行修复操作。
- 如果某个位置的字符不可修改(即
代码实现
public static int solution(int n, int m, String str1, String str2) {
int maxLen = 0;
// 遍历所有可能的字符 'a' 到 'z'
for (char targetChar = 'a'; targetChar <= 'z'; targetChar++) {
int left = 0, right = 0;
int repairs = 0;
while (right < n) {
// 如果当前字符不是目标字符,且可以修改
if (str1.charAt(right) != targetChar && str2.charAt(right) == '1') {
repairs++;
}
// 如果当前字符不是目标字符,且不可修改,则重置窗口
if (str1.charAt(right) != targetChar && str2.charAt(right) == '0') {
left = right + 1;
repairs = 0;
}
// 如果修复次数超过 m,移动左指针
while (repairs > m) {
if (str1.charAt(left) != targetChar && str2.charAt(left) == '1') {
repairs--;
}
left++;
}
// 计算当前窗口的长度
maxLen = Math.max(maxLen, right - left + 1);
right++;
}
}
return maxLen;
}
滑动窗口算法的使用建议
滑动窗口技术常用于处理涉及连续子数组或子串的问题,尤其是在需要动态计算某些统计值(如最大值、最小值、和等)的场景中。常见应用包括查找固定长度子串的最大和、最长不重复子串、子数组和等。使用滑动窗口时,窗口的大小可以是固定的,也可以根据条件动态调整,从而避免了重复计算,提高了算法的效率。