1. 题目描述
这个问题的目标是给定一个由小写字母组成的字符串 str1 和一个限制条件字符串 str2,我们可以通过最多 m 次修改,将 str1 中的一些字符更改为任意小写字母。str2 指定了 str1 中哪些位置的字符是可以修改的,如果 str2[i] 为 1,则 str1[i] 可以被修改,否则不能修改。我们要找出经过最多 m 次修改后,字符串中由相同字符组成的最长连续子串的最大长度。
2. 解题思路
要解决这个问题,我们可以用滑动窗口(sliding window)和双指针(two-pointer)的方法。具体解题思路如下:
- 遍历每一个字母:对于每一个小写字母(从
a到z),我们尝试将str1中的字符尽可能变成这个字母,计算形成最大长度的连续子串。 - 滑动窗口的使用:在字符串上使用滑动窗口保持一个当前字母的子串,保持窗口中的字符一致,如果不一致且允许修改(即
str2[i] = 1),我们尝试通过修改使窗口内所有字符都变成目标字母。 - 控制修复操作次数:我们需要统计窗口内需要修改的字符数(记为
modifications),如果modifications超过了m,说明这个窗口不能满足要求,我们需要收缩窗口,即移动窗口左边界,直到窗口内的modifications数小于等于m。 - 记录最大长度:对于每个字符(
a到z),我们都尝试计算它能形成的最长连续子串长度,最终取所有字符中能够形成的最长长度作为答案。
这种方法的核心在于控制窗口的大小和内部字符的一致性,以最大化目标字母的连续子串长度。由于我们对每个字母都要进行尝试,这种方法的时间复杂度为 O(26 * n)。
3. 代码实现
以下是代码的 Python 实现:
def max_continuous_char_length(n, m, str1, str2):
max_length = 0
# 遍历每一个目标字符,从 'a' 到 'z'
for target_char in 'abcdefghijklmnopqrstuvwxyz':
left = 0 # 窗口的左指针
modifications = 0 # 当前窗口中的修复操作计数
# 使用滑动窗口法调整窗口的右边界
for right in range(n):
# 如果当前字符不是目标字符且可以修改,增加修复计数
if str1[right] != target_char:
if str2[right] == '1':
modifications += 1
else:
# 如果当前字符不符合目标且不能修改,重置窗口
left = right + 1
modifications = 0
continue
# 如果修复操作次数超过 m,收缩窗口
while modifications > m:
if str1[left] != target_char and str2[left] == '1':
modifications -= 1
left += 1
# 计算当前窗口长度,并更新最大长度
max_length = max(max_length, right - left + 1)
return max_length
# 测试用例
print(max_continuous_char_length(5, 2, "abcda", "01110")) # 输出: 3
print(max_continuous_char_length(7, 2, "abbaccb", "1001001")) # 输出: 4
print(max_continuous_char_length(3, 0, "aab", "101")) # 输出: 2
4. 代码详细解释
让我们逐步解释代码的实现细节:
-
外层循环遍历每个目标字符:在
for target_char in 'abcdefghijklmnopqrstuvwxyz'中,我们逐一选择每个字母a到z作为目标字符,试图将str1中的字符尽量转化为这个字符,以形成最大长度的连续子串。 -
滑动窗口和双指针设置:定义
left为窗口的左指针起始位置,modifications记录窗口中需要的修复次数,初始值为 0。 -
右指针遍历
str1:右指针right从左到右遍历字符串str1:-
检查当前字符
str1[right]是否是目标字符target_char:- 如果是,则无需修复。
- 如果不是,则根据
str2[right]检查是否可以修复。如果可以修复,增加modifications计数。如果不可以修复,则说明无法继续扩展此窗口,所以需要将窗口左指针left移动到当前right + 1,并重置modifications。
-
-
控制修复操作次数:在窗口滑动过程中,保持
modifications的值小于等于m。一旦modifications超过m,我们需要缩小窗口,即移动左指针left,并减少modifications。 -
更新最大长度:在每个合法的窗口(即满足
modifications <= m的窗口)中,计算窗口的长度right - left + 1,并更新最大长度max_length。 -
返回最大长度:最终,遍历完所有字母后,
max_length存储的就是满足条件的最大连续子串长度。
5. 时间复杂度和空间复杂度
- 时间复杂度:代码的主要开销在于遍历所有小写字母(26 个)和字符串
str1中的字符,因此时间复杂度为O(26 * n),即O(n)的线性复杂度。 - 空间复杂度:代码没有使用额外的数据结构来存储中间结果,除了常数个变量外,空间复杂度为
O(1)。
6. 总结
这个问题是滑动窗口和双指针的经典应用。滑动窗口技巧非常适合这种寻找连续子数组或子串的问题。在本题中,我们借助滑动窗口和修改次数控制来实现如下目标:
- 最大化连续子串的长度:通过遍历每个可能的目标字符,找到能够满足条件的最长连续子串。
- 使用修复操作灵活调整窗口:在每个字符的尝试中,通过统计修复次数实现动态调整窗口大小,从而找到符合修复限制的最优解。
- 优化搜索空间:遍历26个字母并在每个字母上使用滑动窗口,不需要复杂的数据结构即可高效完成解题。
这种方法高效且简洁,易于实现。通过该题,我们也可以掌握滑动窗口在限制条件下求解连续子串的基本思想,为解决其他类似问题提供了思路。