题目解析:字符修复问题 | 豆包MarsCode AI刷题

153 阅读5分钟

1. 题目描述

这个问题的目标是给定一个由小写字母组成的字符串 str1 和一个限制条件字符串 str2,我们可以通过最多 m 次修改,将 str1 中的一些字符更改为任意小写字母。str2 指定了 str1 中哪些位置的字符是可以修改的,如果 str2[i]1,则 str1[i] 可以被修改,否则不能修改。我们要找出经过最多 m 次修改后,字符串中由相同字符组成的最长连续子串的最大长度。

2. 解题思路

要解决这个问题,我们可以用滑动窗口(sliding window)和双指针(two-pointer)的方法。具体解题思路如下:

  • 遍历每一个字母:对于每一个小写字母(从 az),我们尝试将 str1 中的字符尽可能变成这个字母,计算形成最大长度的连续子串。
  • 滑动窗口的使用:在字符串上使用滑动窗口保持一个当前字母的子串,保持窗口中的字符一致,如果不一致且允许修改(即 str2[i] = 1),我们尝试通过修改使窗口内所有字符都变成目标字母。
  • 控制修复操作次数:我们需要统计窗口内需要修改的字符数(记为 modifications),如果 modifications 超过了 m,说明这个窗口不能满足要求,我们需要收缩窗口,即移动窗口左边界,直到窗口内的 modifications 数小于等于 m
  • 记录最大长度:对于每个字符(az),我们都尝试计算它能形成的最长连续子串长度,最终取所有字符中能够形成的最长长度作为答案。

这种方法的核心在于控制窗口的大小和内部字符的一致性,以最大化目标字母的连续子串长度。由于我们对每个字母都要进行尝试,这种方法的时间复杂度为 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. 代码详细解释

让我们逐步解释代码的实现细节:

  1. 外层循环遍历每个目标字符:在 for target_char in 'abcdefghijklmnopqrstuvwxyz' 中,我们逐一选择每个字母 az 作为目标字符,试图将 str1 中的字符尽量转化为这个字符,以形成最大长度的连续子串。

  2. 滑动窗口和双指针设置:定义 left 为窗口的左指针起始位置,modifications 记录窗口中需要的修复次数,初始值为 0。

  3. 右指针遍历 str1:右指针 right 从左到右遍历字符串 str1

    • 检查当前字符 str1[right] 是否是目标字符 target_char

      • 如果是,则无需修复。
      • 如果不是,则根据 str2[right] 检查是否可以修复。如果可以修复,增加 modifications 计数。如果不可以修复,则说明无法继续扩展此窗口,所以需要将窗口左指针 left 移动到当前 right + 1,并重置 modifications
  4. 控制修复操作次数:在窗口滑动过程中,保持 modifications 的值小于等于 m。一旦 modifications 超过 m,我们需要缩小窗口,即移动左指针 left,并减少 modifications

  5. 更新最大长度:在每个合法的窗口(即满足 modifications <= m 的窗口)中,计算窗口的长度 right - left + 1,并更新最大长度 max_length

  6. 返回最大长度:最终,遍历完所有字母后,max_length 存储的就是满足条件的最大连续子串长度。

5. 时间复杂度和空间复杂度

  • 时间复杂度:代码的主要开销在于遍历所有小写字母(26 个)和字符串 str1 中的字符,因此时间复杂度为 O(26 * n),即 O(n) 的线性复杂度。
  • 空间复杂度:代码没有使用额外的数据结构来存储中间结果,除了常数个变量外,空间复杂度为 O(1)

6. 总结

这个问题是滑动窗口和双指针的经典应用。滑动窗口技巧非常适合这种寻找连续子数组或子串的问题。在本题中,我们借助滑动窗口和修改次数控制来实现如下目标:

  1. 最大化连续子串的长度:通过遍历每个可能的目标字符,找到能够满足条件的最长连续子串。
  2. 使用修复操作灵活调整窗口:在每个字符的尝试中,通过统计修复次数实现动态调整窗口大小,从而找到符合修复限制的最优解。
  3. 优化搜索空间:遍历26个字母并在每个字母上使用滑动窗口,不需要复杂的数据结构即可高效完成解题。

这种方法高效且简洁,易于实现。通过该题,我们也可以掌握滑动窗口在限制条件下求解连续子串的基本思想,为解决其他类似问题提供了思路。