问题描述
有一个小写字母组成的字符串
str,字符串长度为n;字符串中某些位置可以执行修复操作:将这个位置的字符,替换为a~z中的一个字符;这个修复操作最多可以执行m次;现在想知道修复之后,字符串str由相同字符组成的子串最大长度是多少
输入格式
每个样例有三行:
- 第一行是整数
n和m,分别表示字符串长度和最多操作次数;(1≤n≤2000,0≤m≤20001≤n≤2000,0≤m≤2000)- 第二行是字符串
str;- 第三行是长度为
n的 01 字符串,0表示该位置的字符无法修改,1表示该位置的字符可以修改;
输出格式
每个样例一行,输出修复之后由相同字符组成的子串最大长度;
第一次思考,我忽略了一个边界问题,我把第一个字符提前计入计数范围了,所以导致错误。后来经过参考其他人的答案以及经过MarsCode的帮助,我得到了成功的解决答案。
数据结构选择
- 字符串:用于存储输入的字符串
str。 - 字符数组:用于存储哪些位置的字符可以被修改(
canModify)。
算法步骤
-
遍历目标字符:
- 对于每个字符 target_char 从 'a' 到 'z',尝试将其作为目标字符。这一步的目的是为了找到以每个字符为目标字符时,能够得到的最长连续子串。
-
滑动窗口法:
- 使用两个指针
left和right来表示当前窗口的左右边界。 - 初始化
left为 0,modifications为 0。
- 使用两个指针
-
扩展窗口:
-
遍历字符串
str,对于每个字符str[right]:- 如果
str[right]不是目标字符且可以修改(canModify[right] == '1'),则增加modifications计数。 - 如果
str[right]不是目标字符且不能修改(canModify[right] == '0'),则重置窗口,将left移动到right + 1,并重置modifications为 0。
- 如果
-
-
收缩窗口:
- 如果 modifications 超过 m,则收缩窗口,移动 left 指针直到 modifications 不超过 m。 在收缩窗口时,如果 str[left] 不是目标字符且可以修改(canModify[left] == '1'),则减少 modifications 计数。
-
更新最大长度:
- 计算当前窗口的长度
right - left + 1,并更新最大长度max_length。
- 计算当前窗口的长度
详细代码
def solution(n, m, str, canModify):
max_length = 0
for target_char in 'abcdefghijklmnopqrstuvwxyz':
left = 0
modifications = 0
for right in range(n):
if str[right] != target_char:
if canModify[right] == '1':
modifications += 1
else:
left = right + 1
modifications = 0
continue
while modifications > m:
if str[left] != target_char and canModify[left] == '1':
modifications -= 1
left += 1
max_length = max(max_length, right - left + 1)
return max_length
if __name__ == "__main__":
# Add your test cases here
print(solution(5, 2, "abcda", "01110") == 3)
print(solution(7, 2, "abbaccb", "1001001") == 4)
print(solution(3, 0, "aab", "101") == 2)
总结
这个问题是滑动窗口和双指针的经典应用。滑动窗口技巧非常适合这种寻找连续子数组或子串的问题。
- 核心思想:使用滑动窗口法来动态调整窗口大小,以找到最大长度的相同字符子串。
- 时间复杂度:O(26 * n),其中 26 是字母表的大小,n 是字符串的长度。
- 空间复杂度:O(1),只使用了常数级别的额外空间。