String Compression
Recibes una cadena s de letras inglesas. Comprímela: reemplaza cada secuencia de dos o más letras iguales consecutivas por esa letra seguida de la longitud de la secuencia, y deja tal cual las letras que aparecen solas. Así, aabccc se convierte en a2bc3. Devuelve la cadena comprimida.
Las mayúsculas y las minúsculas son letras diferentes: aA son dos secuencias de longitud 1.
Función
- sstring
- la cadena que se va a comprimir, solo letras inglesas
- Devuelvestring
- la cadena comprimida, cada secuencia escrita como su letra seguida de su longitud cuando la longitud es mayor que 1
Restricciones
1 ≤ s.length ≤ 2 × 104scontiene solo letras inglesas, de laaa lazy de laAa laZ.- La longitud de una secuencia se escribe en decimal y puede tener varios dígitos.
Ejemplos
- Entrada
- s = "aabccc"
- Salida
- "a2bc3"
- Explicación
- Las rachas son
aa,byccc. Se convierten ena2,b(una letra sola no lleva contador) yc3.
- Entrada
- s = "abbbbbbbbbbbb"
- Salida
- "ab12"
- Explicación
- Una
a, después doceb. La longitud 12 se escribe con los dos caracteres1y2, así que el resultado esab12.
- Entrada
- s = "zzZZZ"
- Salida
- "z2Z3"
- Explicación
- La letra
zminúscula y la letraZmayúscula son diferentes, así que esto es una secuencia de dos seguida de una secuencia de tres:z2Z3.
Para ir más allá
Escribe la operación inversa: dado un texto comprimido como ab12, reconstruye el original. ¿Cómo lees un número de repeticiones que tiene varios dígitos?
Pistas
Ábrelas de una en una. Cada una revela un poco más.
Lee
scomo una fila de bloques, cada uno formado por una letra repetida. ¿Qué dos datos sobre un bloque necesitas para escribirlo?Cada bloque necesita su letra y su longitud. Mantén un índice donde empieza el bloque y avanza un segundo índice mientras siga viendo la misma letra; la distancia entre ambos es la longitud.
Mantén
ial inicio de una secuencia y muevejhasta ques[j]sea distinto o la cadena termine. Escribes[i]y despuésj-icomo texto decimal cuando sea mayor que 1, y salta conihastaj. Una longitud como 12 tiene dos caracteres.
Solución
Encuentra dónde empieza y termina cada secuencia de letras iguales y luego escribe su longitud como texto. Las secuencias de una sola letra no llevan contador, y un contador puede tener varios dígitos. Dos índices, uno al inicio de una secuencia y otro que recorre hasta su final, lo hacen en una sola pasada.
Recopila las ejecuciones y después escríbelas
Intuición
Primero, registra las secuencias como pares de una letra y una longitud: para aabccc, (a, 2), (b, 1), (c, 3). Cada letra extiende la última secuencia o inicia una nueva. Después, escribe la letra de cada secuencia, seguida de su longitud cuando sea mayor que 1.
Ambos recorridos son O(n). La lista puede contener n secuencias cuando no coinciden dos elementos vecinos, así que requiere O(n) espacio adicional.
Algoritmo
- Crea una lista vacía de secuencias.
- Para cada letra de
s: si la última secuencia tiene la misma letra, suma 1 a su longitud; de lo contrario, añade una nueva secuencia de longitud 1. - Para cada secuencia, escribe su letra y después su longitud como texto decimal si la longitud es mayor que 1.
def compress(s):
# First pass: collect the runs as [letter, length] pairs
runs = []
for ch in s:
if runs and runs[-1][0] == ch:
runs[-1][1] += 1
else:
runs.append([ch, 1])
# Second pass: write each run, with its length when it is more than 1
parts = []
for letter, length in runs:
parts.append(letter)
if length > 1:
parts.append(str(length))
return "".join(parts)Dos punteros sobre cada secuencia
Intuición
Mantén i en la primera letra de la secuencia actual y avanza j mientras s[j] sea igual a s[i]. Cuando j se detenga, ante una letra distinta o al final, la longitud de la secuencia es j-i. Escríbela y asigna i = j. En abbbbbbbbbbbb, j se detiene primero en 1, dando a, y luego en 13, dando b12.
La última secuencia no requiere un caso especial, porque el bucle interno se detiene al llegar al final de la cadena. j solo avanza, así que el tiempo es O(n), y además del resultado conservas dos índices. Las versiones de R y Swift dividen s primero en un arreglo de caracteres, así que usan O(n) de espacio adicional.
Algoritmo
- Establece
i = 0. - Mientras
iesté dentro de la cadena, establecej = iy avanzajmientras esté dentro de la cadena ys[j]sea igual as[i]. - Escribe
s[i]; sij-ies mayor que 1, escríbelo como texto decimal. - Establece
i = jy repite.
def compress(s):
parts = []
n = len(s)
i = 0
while i < n:
# Move j to the first index after the run that starts at i
j = i
while j < n and s[j] == s[i]:
j += 1
parts.append(s[i])
if j - i > 1:
parts.append(str(j - i))
i = j
return "".join(parts)
Errores comunes y casos límite
- Escribir una secuencia solo cuando encuentras una letra diferente. No hay nada después del
cccfinal deaabccc, así que devuelvesa2b. - Escribir el recuento como un solo carácter, como
'0' + count. Falla al llegar a 10: se necesitan los dos caracteres12para doceb. - Escribir
1después de una letra aislada.bse queda comob, no comob1. - Ignorar las mayúsculas y minúsculas.
zzZZZesz2Z3, noz5. - Hacer crecer una cadena inmutable con
+, como en Java o C#. Cada+copia el resultado obtenido hasta ese momento, lo que hace que el bucle tenga una complejidad deO(n²); usa un constructor de cadenas.
Preguntas frecuentes3
¿Cuál es la complejidad temporal de la compresión de cadenas?
El recorrido con dos punteros tarda O(n), porque el índice de exploración solo avanza. Además del resultado, mantiene dos índices, por lo que el espacio adicional es O(1).
¿Puede la cadena comprimida ser más larga que la original?
No en esta versión. Una letra aislada sigue siendo un carácter, y una secuencia de k ≥ 2 letras se convierte en una letra más los dígitos de k, nunca más de k caracteres. Un esquema que escribe un recuento después de cada letra, como a1b1, puede duplicar la longitud.
¿Cómo comprimes una cadena in situ?
Usa un arreglo de caracteres con un índice de lectura y un índice de escritura. Recorre cada secuencia con el índice de lectura y después escribe su letra y los dígitos de su longitud en el índice de escritura. La salida nunca es más larga que la entrada leída hasta el momento, así que el índice de escritura nunca supera al índice de lectura. Devuelve la nueva longitud al final.
Problemas similares
Problemas que usan las mismas ideas. Resolver dos o tres es lo que fija un patrón.
def compress(s):
# Escribe el código aquíPrueba #1
Prueba #2
Prueba #3
Argumento
s = "aabccc"
Salida Esperada
"a2bc3"