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

Diagramas de flujo

Diagrama de flujo (actividad) del ejercicio 05

Diagrama de flujo (grafo) del ejercicio 05

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}")

Ejecútalo en el navegador

▶ Visualízalo paso a paso en Python Tutor — ve cómo cambian las variables línea a línea.

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