python - leetcode - 424. El carácter repetido más largo después del reemplazo [Solución clásica: algoritmo de ventana deslizante codiciosa]

1. Título: 424. El carácter repetido más largo después del reemplazo.

Descripción:
Se le proporciona una cadena s y un número entero k. Puede seleccionar cualquier carácter de la cadena y cambiarlo por cualquier otro carácter en mayúscula en inglés. Esta operación se puede realizar como máximo k veces.

Después de realizar las operaciones anteriores, devuelva la longitud de la subcadena más larga que contenga la misma letra.

Ejemplo 1:

输入:s = "ABAB", k = 2
输出:4
解释:用两个'A'替换为两个'B',反之亦然。

Ejemplo 2:

输入:s = "AABABBA", k = 1
输出:4
解释:
将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子串 "BBBB" 有最长重复字母, 答案为 4。
可能存在其他的方法来得到同样的结果。

pista:

  • 1 <= s.longitud <= 105
  • s consta únicamente de letras mayúsculas en inglés
  • 0 <= k <= s.longitud

2. Ideas para resolver problemas

Primero, es necesario distinguir dos conceptos: subcadena (submatriz) y subsecuencia. Estos dos términos aparecen a menudo en las preguntas y es necesario distinguirlos. Una subcadena (submatriz) es continua, mientras que una subsecuencia puede ser discontinua.

Otra expresión : encuentre el intervalo más largo en una cadena, en el que el número de caracteres que aparecen con menos frecuencia no exceda k.

La expresión anterior es equivalente a la pregunta. La ventaja de abstraerla en esta expresión es que nos permite saber que se trata de un problema de intervalo. El método que se utiliza a menudo para encontrar subintervalos es el puntero doble.

"Método de captura de insectos": El proceso de mover los punteros dobles es muy similar al proceso de gatear de un insecto: si el pie delantero no se mueve, mueva el pie trasero; si el pie trasero no se mueve, mueva el pie delantero hacia adelante.

Explique por qué se llama "ventana corrediza codiciosa", porque además de "deslizarse", también puede "expandirse", que también es el elemento central de la solución a este problema.

  • Deslizante: los extremos izquierdo y derecho de la ventana se mueven una posición hacia la derecha al mismo tiempo.
  • Expansión: el extremo izquierdo de la ventana se fija y el extremo derecho se mueve una posición hacia la derecha.

1. ¿Cómo inicializar la ventana dinámica?

Respuesta: De manera similar a la idea de programación dinámica, primero inicializamos una ventana con una longitud de 0 y comenzamos a recorrer desde el lado izquierdo de la cadena.

2. ¿Cuándo expandirse?

Respuesta: Cuando la subcadena cumpla con los requisitos, expanda un bit hacia la derecha (codicioso, la subcadena continuará expandiéndose después de cumplir con los requisitos) Pregunta:
Cuando la subcadena cumpla con los requisitos (solo contiene caracteres repetidos)
Respuesta de seguimiento: win_len - max_freq <= k
Explicación: El número de reemplazos k es suficiente para reemplazar todos los caracteres en la ventana actual con los caracteres de mayor frecuencia

3. ¿Cuándo deslizar el dedo?

Respuesta: Cuando la subcadena no cumple con los requisitos, deslice una posición hacia la derecha en su totalidad (búsqueda),
es decir, win_len - max_freq > k.
Tenga en cuenta que la longitud de la ventana no se reducirá. Incluso si no se puede encontrar una subcadena mejor Si se busca más tarde, no afectará a la solución óptima de la diapositiva anterior.

La plantilla de doble puntero puede resolver la mayoría de los problemas de doble puntero:

def findSubstring(s):
    N = len(s) # 数组/字符串长度
    left, right = 0, 0 # 双指针,表示当前遍历的区间[left, right],闭区间
    counter = collections.Counter() # 用于统计 子数组/子区间 是否有效
    res = 0 # 保存最大的满足题目要求的 子数组/子串 长度
    while right < N: # 当右边的指针没有搜索到 数组/字符串 的结尾
        counter[s[right]] += 1 # 增加当前右边指针的数字/字符的计数
        while 区间[left, right]不符合题意:# 此时需要一直移动左指针,直至找到一个符合题意的区间
            counter[s[left]] -= 1 # 移动左指针前需要从counter中减少left位置字符的计数
            left += 1 # 真正的移动左指针,注意不能跟上面一行代码写反
        # 到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
        res = max(res, right - left + 1) # 需要更新结果
        right += 1 # 移动右指针,去探索新的区间
    return res

3. Ejemplos de código

class Solution(object):
    def characterReplacement(self, s, k):
        """
        :type s: str
        :type k: int
        :rtype: int
        """
        # 解法一
        left = 0
        right = 0
        dic = collections.defaultdict(int)
        maxcount = 0
        if len(s) == 0:
            return 0
        for right in range(len(s)):
            dic[s[right]] += 1
            maxcount = max(maxcount,dic[s[right]])
            if right - left + 1 - maxcount > k:
                dic[s[left]] -= 1
                left += 1
        return right - left + 1

		# 解法二
        N = len(s)
        left, right = 0, 0 # [left, right] 都包含
        counter = collections.Counter()
        res = 0
        while right < N:
            counter[s[right]] += 1
            while right - left + 1 - counter.most_common(1)[0][1] > k:
                counter[s[left]] -= 1
                left += 1
            res = max(res, right - left + 1)
            right += 1
        return res

		# 解法三
        count = [0 for _ in range(26)]  # 记录当前窗口的字母出现次数
        left = 0  # 滑动窗口左边界
        right = 0  # 滑动窗口右边界
        retval = 0  # 最长窗口长度

        while right < len(s):
            count[ord(s[right]) - ord('A')] += 1
            benchmark = max(count)  # 选择出现次数最多的字母为基准
            others = sum(count) - benchmark  # 则其他字母需要通过替换操作来变为基准
            if others <= k:  # 通过与K进行比较来判断窗口是进行扩张?
                right += 1
                retval = max(retval, right - left)  # 记录当前有效窗口长度
            else:  # 通过与K进行比较来判断窗口还是进行位移?
                count[ord(s[left]) - ord('A')] -= 1
                left += 1
                right += 1  # 这里注意:位移操作需要整个向右移,不仅仅只是left向右

        return retval

Supongo que te gusta

Origin blog.csdn.net/qq_43030934/article/details/131677911
Recomendado
Clasificación