Ejercicio 05 — Dadas dos cadenas s y t, devuelve True si t es un anagrama de s

Dificultad: verde · Módulo 09 (Puente a NeetCode)

Enunciado

dadas dos cadenas s y t, devuelve True si t es un anagrama de s

Cómo se resuelve

Dos palabras son anagramas si tienen las mismas letras con las mismas frecuencias: el patrón es contar apariciones, y collections.Counter lo hace en una línea.

  1. if len(s) != len(t): return False — atajo barato. Si tienen distinto número de caracteres, es imposible que sean anagramas, y así evitamos trabajo innecesario.
  2. Counter(s) construye un dict {caracter: frecuencia} recorriendo la cadena. Counter(s) == Counter(t) es True cuando ambos tienen exactamente las mismas claves con los mismos valores. Todo en O(n) tiempo y O(k) espacio (k = letras distintas).
  3. La versión manual muestra lo que hace Counter por dentro: conteo[c] = conteo.get(c, 0) + 1 suma uno por cada letra de s, y luego - 1 por cada letra de t. get(c, 0) devuelve 0 si la clave aún no existe, evitando el KeyError.
  4. all(v == 0 for v in conteo.values()) cierra la idea: si sumar s y restar t deja todos los contadores a cero, cada letra apareció el mismo número de veces en ambas. Si alguno queda distinto de cero, hay desajuste.

Trampa habitual: usar sorted(s) == sorted(t). Funciona, pero ordenar cuesta O(n log n); el conteo con dict/Counter es O(n). En una entrevista, el conteo es la respuesta que buscan por ser más eficiente.

Para practicar — cópialo y complétalo

Pega este esqueleto y completa los TODO. Es la mejor forma de aprender: inténtalo antes de mirar la solución.

# Curso de Python — Modulo 09: Puente a NeetCode
# Ejercicio 05 — PRACTICA (rellena los TODO)
# Enunciado: dadas dos cadenas s y t, devuelve True si t es un anagrama de s
# Dificultad: verde
# Ejecutar: python3 ej05_practica.py
 
# PISTA: collections.Counter(cadena) devuelve un dict {caracter: frecuencia}.
# Dos Counter son iguales con == si tienen las mismas letras con las mismas frecuencias.
# Bonus: intenta tambien la version manual con dict y .get(c, 0).
 
 
from collections import Counter
 
 
def is_anagram(s: str, t: str) -> bool:
    """Devuelve True si t es un anagrama de s."""
    # TODO 1: si len(s) != len(t) devuelve False directamente
    if ...:
        return ...
 
    # TODO 2: compara Counter(s) con Counter(t) y devuelve el resultado
    return ...
 
 
# --- Pruebas (no toques esta parte) ---
if __name__ == "__main__":
    casos = [
        ("anagram", "nagaram", True),
        ("rat",     "car",     False),
        ("a",       "a",       True),
        ("ab",      "a",       False),
    ]
 
    for s, t, esperado in casos:
        resultado = is_anagram(s, t)
        estado = "OK" if resultado == esperado else "FALLO"
        print(f"[{estado}] is_anagram({s!r}, {t!r}) = {resultado}  (esperado {esperado})")

Solución — cópiala y ejecútala

# Curso de Python — Modulo 09: Puente a NeetCode
# Ejercicio 05 — MODELO (resuelto)
# Enunciado: dadas dos cadenas s y t, devuelve True si t es un anagrama de s
# Dificultad: verde
# Ejecutar: python3 ej05_modelo.py
 
# PATRON: conteo de frecuencias con collections.Counter.
# Dos cadenas son anagramas si tienen exactamente las mismas letras
# con las mismas frecuencias. Counter crea un dict {caracter: frecuencia}
# y dos Counter son iguales (==) si tienen las mismas claves y valores.
#
# ALTERNATIVA MANUAL (sin Counter): usar un dict propio para contar.
# La mostramos como version 2 para entender que hace Counter por dentro.
 
 
from collections import Counter
 
 
def is_anagram(s: str, t: str) -> bool:
    """Devuelve True si t es un anagrama de s."""
    # Longitudes distintas -> imposible ser anagrama
    if len(s) != len(t):
        return False
 
    return Counter(s) == Counter(t)
 
 
def is_anagram_manual(s: str, t: str) -> bool:
    """Misma logica, implementada a mano con dict para entender el mecanismo."""
    if len(s) != len(t):
        return False
 
    conteo = {}
 
    # Sumamos 1 por cada letra en s
    for c in s:
        conteo[c] = conteo.get(c, 0) + 1
 
    # Restamos 1 por cada letra en t
    for c in t:
        conteo[c] = conteo.get(c, 0) - 1
 
    # Si algun valor no es 0, hay diferencia de frecuencias
    return all(v == 0 for v in conteo.values())
 
 
# --- Pruebas manuales ---
if __name__ == "__main__":
    casos = [
        ("anagram", "nagaram", True),
        ("rat",     "car",     False),
        ("a",       "a",       True),
        ("ab",      "a",       False),
    ]
 
    for s, t, esperado in casos:
        r1 = is_anagram(s, t)
        r2 = is_anagram_manual(s, t)
        e1 = "OK" if r1 == esperado else "FALLO"
        e2 = "OK" if r2 == esperado else "FALLO"
        print(f"[{e1}] Counter: {r1}   [{e2}] manual: {r2}   s={s!r} t={t!r}")

Cómo usarlo

Pega el código en un fichero y ejecútalo con tu toolchain habitual (o el botón de Ejecutar de tu editor). Antes de mirar la solución, intenta completar tú el esqueleto: es la mejor forma de aprender.

Conexiones