- 题目:小R有一个由小写字母组成的字符串
str1,长度为n。字符串中有些位置的字符可以通过修复操作来修改为a-z中的任意一个字母,这个修复操作最多可以进行m次。现在,小R想知道,经过最多m次修复操作后,字符串中由相同字符组成的最长连续子串的最大长度是多少。
注意,str2表示哪些下标可以修改,如果为1那么就可以修改,如果为0那么就不行。
- 解题思路:
- 我们可以使用滑动窗口的方法来解决这个问题。滑动窗口的思想是通过不断调整窗口的左右边界,来找到满足特定条件的子串或子数组等。
- 具体到这个问题,我们可以通过移动窗口来遍历字符串,同时利用修复操作的次数限制 m 来更新窗口内的字符,以尝试找到由相同字符组成的最长连续子串的最大长度。
- 首先,我们初始化两个指针 left 和 right ,分别表示滑动窗口的左右边界,初始时都指向字符串的开头,即 left = right = 0 。
- 同时,我们需要一个字典 char_count 来记录窗口内每个字符出现的次数,一个变量 modify_count 来记录已经使用的修复操作次数。
- 然后,我们开始移动 right 指针来扩大窗口,每次移动 right 指针时:
- 如果当前字符 str1[right] 所在位置可以修改(即 str2[right] == 1 ),我们就检查是否需要对其进行修改以使得窗口内的字符更有可能形成更长的相同字符连续子串。
- 如果当前字符不需要修改或者不能修改,我们就更新 char_count 字典中该字符的出现次数。
- 在移动 right 指针的过程中,如果发现窗口内不同字符的数量大于 1 ,并且还有剩余的修复操作次数 m ,我们就尝试通过修改字符来使得窗口内的字符相同。具体做法是:从窗口的左边开始,找到可以修改的字符位置,将其修改为窗口内出现次数最多的字符,同时更新 char_count 字典和 modify_count 变量。
- 当窗口内的字符都相同时,我们就可以更新最长连续子串的长度记录,即 max_length 。
- 最后,通过不断移动 right 指针扩大窗口,然后根据情况适当移动 left 指针收缩窗口,直到 right 指针到达字符串的末尾,此时 max_length 就是经过最多 m 次修复操作后,字符串中由相同字符组成的最长连续子串的最大长度。
- 示例图示(以简单示例说明滑动窗口过程):
假设我们有字符串 str1 = "abca" , str2 = [1, 1, 0, 1] ,表示第1、2、4个位置的字符可以修改,修复操作次数 m = 2 。
- 初始时, left = 0 , right = 0 , char_count = {'a': 1} , modify_count = 0 ,窗口为 "a" ,最长连续子串长度 max_length = 1 。
- 移动 right 指针,当 right = 1 时,因为 str2[1] == 1 (可以修改),且当前窗口内字符不同( 'a' 和 'b' ),我们可以考虑修改 'b' 为 'a' ,此时 char_count = {'a': 2} , modify_count = 1 ,窗口为 "aa" ,最长连续子串长度更新为 2 。
- 继续移动 right 指针,当 right = 2 时,因为 str2[2] == 0 (不能修改),所以 char_count = {'a': 2, 'c': 1} ,窗口内字符不同,且此时已经使用了1次修复操作,还剩1次修复操作。我们可以从窗口左边找可以修改的字符位置(这里就是第1个位置),将其修改为窗口内出现次数最多的字符(还是 'a' ),更新后 char_count = {'a': 3} , modify_count = 2 ,窗口为 "aaa" ,最长连续子串长度更新为 3 。
- 当 right = 3 时,因为 str2[3] == 1 (可以修改),且当前窗口内字符已经都是 'a' ,无需修改,最长连续子串长度保持为 3 。
Python代码如下:
`from typing import List
def max_same_char_substring_length(str1: str, str2: List[int], m: int) -> int:
n = len(str1)
left = right = 0
char_count = {}
modify_count = 0
max_length = 0
while right < n:
if str2[right] == 1:
if str1[right] not in char_count:
char_count[str1[right]] = 1
else:
char_count[str1[right]] += 1
else:
if str1[right] not in char_count:
char_count[str1[right]] = 1
else:
char_count[str1[right]] += 1
while len(char_count) > 1 and modify_count < m:
if str2[left] == 1:
char_to_modify = str1[left]
char_count[char_to_modify] -= 1
if char_count[char_to_modify] == 0:
del char_count[char_to_modify]
most_common_char = max(char_count, key=char_count.get)
char_count[most_common_char] += 1
str1 = str1[:left] + most_common_char + str1[left + 1:]
modify_count += 1
left += 1
if len(char_count) == 1:
max_length = max(max_length, sum(char_count.values()))
right += 1
return max_length
`
- 代码详解:
- max_same_char_substring_length 函数接受三个参数: str1 是由小写字母组成的原始字符串, str2 是一个列表,用于指示字符串 str1 中哪些位置的字符可以修改, m 是允许的修复操作次数。函数返回经过最多 m 次修复操作后,字符串中由相同字符组成的最长连续子串的最大长度。
- 首先,获取字符串 str1 的长度 n ,并初始化滑动窗口的左右指针 left 和 right 都为0,初始化字典 char_count 用于记录窗口内每个字符出现的次数,变量 modify_count 用于记录已经使用的修复操作次数,以及变量 max_length 用于记录最长连续子串的长度,初始值都为相应的默认值。
- 然后,进入一个 while 循环,只要 right 指针小于字符串的长度 n ,就继续循环。
- 在每次循环中,首先判断当前字符 str1[right] 所在位置是否可以修改(即 str2[right] == 1 ):
- 如果可以修改,且当前字符不在 char_count 字典中,就将其添加到字典中并设置出现次数为1;如果已经在字典中,就将其出现次数加1。
- 如果不能修改,同样的操作,将当前字符添加到字典中并更新出现次数。
- 接着,进入一个内层 while 循环,当窗口内不同字符的数量大于1并且已经使用的修复操作次数 modify_count 小于允许的修复操作次数 m 时,就尝试通过修改字符来使得窗口内的字符相同。
- 首先判断窗口左边的字符是否可以修改(即 str2[left] == 1 ),如果可以修改,就获取要修改的字符 char_to_modify ,将其在 char_count 字典中的出现次数减1,如果减到0就从字典中删除该字符。
- 然后找到窗口内出现次数最多的字符 most_common_char ,将其在 char_count 字典中的出现次数加1,并将字符串 str1 中对应位置的字符修改为 most_common_char ,同时将 modify_count 加1。
- 最后将 left 指针加1,收缩窗口。
- 当内层 while 循环结束后,如果窗口内的字符都相同时(即 len(char_count) == 1 ),就更新最长连续子串的长度 max_length ,取当前的 max_length 和窗口内字符出现次数总和(即 sum(char_count.values()) )中的最大值。
- 最后,将 right 指针加1,扩大窗口,继续下一次循环。
- 当 right 指针到达字符串的末尾时,函数返回 max_length ,就是经过最多 m 次修复操作后,字符串中由相同字符组成的最长连续子串的最大长度。