14 - Ejemplo práctico 5: palíndromos con historial

14.1 Objetivo

Crear una herramienta que verifique si una cadena es un palíndromo ignorando espacios y mayúsculas, y que permita deshacer/rehacer verificaciones para volver rápidamente a resultados previos.

14.2 Limpieza de texto

  • Quitar espacios, signos de puntuación y convertir a minúsculas.
  • Solo los caracteres alfanuméricos forman parte de la verificación.

14.3 Verificación con pilas

Se usa una pila para comparar simultáneamente los extremos. Dos enfoques:

  1. Dividir la cadena en mitad y apilar la primera mitad.
  2. Usar una pila para recorrer de derecha a izquierda y comparar con el recorrido normal.

El ejemplo a continuación opta por el segundo enfoque para mantenerlo simple.

14.4 Historial con undo/redo

Mantenemos dos pilas de cadenas:

  • Resultados: almacena los textos evaluados junto con el veredicto.
  • Redo: permite rehacer cuando se deshace una evaluación.

Cuando se ejecuta una nueva verificación, se límpia la pila Redo.

14.5 Implementación en C

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#define MAX 256

typedef struct Nodo {
  char *texto;
  struct Nodo *sig;
} Nodo;

typedef struct {
  Nodo *top;
} PilaStr;

static void pila_init(PilaStr *p) { p->top = NULL; }
static int pila_is_empty(const PilaStr *p) { return p->top == NULL; }

static void pila_push(PilaStr *p, const char *texto) {
  Nodo *n = malloc(sizeof(Nodo));
  if (!n) return;
  n->texto = strdup(texto);
  n->sig = p->top;
  p->top = n;
}

static char *pila_pop(PilaStr *p) {
  if (pila_is_empty(p)) return NULL;
  Nodo *tmp = p->top;
  char *texto = tmp->texto;
  p->top = tmp->sig;
  free(tmp);
  return texto;
}

static void pila_clear(PilaStr *p) {
  while (!pila_is_empty(p)) {
    free(pila_pop(p));
  }
}

static void normalizar(const char *entrada, char *salida) {
  int idx = 0;
  for (size_t i = 0; entrada[i]; ++i) {
    if (isalnum((unsigned char)entrada[i])) {
      salida[idx++] = (char)tolower((unsigned char)entrada[i]);
    }
  }
  salida[idx] = '\0';
}

static int es_palindromo(const char *texto) {
  char limpio[MAX];
  normalizar(texto, limpio);
  int len = (int)strlen(limpio);
  for (int i = 0; i < len / 2; ++i) {
    if (limpio[i] != limpio[len - 1 - i]) return 0;
  }
  return 1;
}

typedef struct {
  PilaStr historial;
  PilaStr redo;
} Verificador;

void verificador_init(Verificador *v) {
  pila_init(&v->historial);
  pila_init(&v->redo);
}

void verificador_check(Verificador *v, const char *texto) {
  char resultado[300];
  snprintf(resultado, sizeof(resultado), "\"%s\" => %s", texto,
           es_palindromo(texto) ? "PALINDROMO" : "NO PALINDROMO");
  pila_push(&v->historial, resultado);
  pila_clear(&v->redo);
}

const char *verificador_undo(Verificador *v) {
  if (pila_is_empty(&v->historial)) return "Nada para deshacer";
  char *ultimo = pila_pop(&v->historial);
  pila_push(&v->redo, ultimo);
  free(ultimo);
  return pila_is_empty(&v->historial) ? "Historial vacio" : v->historial.top->texto;
}

const char *verificador_redo(Verificador *v) {
  if (pila_is_empty(&v->redo)) return "Nada para rehacer";
  char *valor = pila_pop(&v->redo);
  pila_push(&v->historial, valor);
  free(valor);
  return v->historial.top->texto;
}

void verificador_clear(Verificador *v) {
  pila_clear(&v->historial);
  pila_clear(&v->redo);
}

int main(void) {
  Verificador v;
  verificador_init(&v);
  verificador_check(&v, "Anita lava la tina");
  verificador_check(&v, "Hola mundo");
  printf("%s\n", verificador_undo(&v));
  printf("%s\n", verificador_redo(&v));
  verificador_check(&v, "Yo hago yoga hoy");
  printf("%s\n", v.historial.top->texto);
  verificador_clear(&v);
  return 0;
}
Historial de palíndromos verificados

14.6 Controles adicionales

  • Añadir una pila separada para mostrar los resultados previos en pantalla.
  • Exportar el historial a un archivo de texto.
  • Agregar soporte para cadenas Unicode con librerías especializadas.