Skip to content
Clases

Lenguajes Formales

En Programación Competitiva, muchos problemas de strings (como validación de paréntesis o búsqueda de patrones) son en realidad problemas de Teoría de Lenguajes.

Un conjunto finito de símbolos.

  • Ejemplo: Σ={a,b,…,z}\Sigma = \{a, b, \dots, z\}

Un conjunto de strings formadas con símbolos de Σ\Sigma.

  • Formalmente: L⊆Σ∗L \subseteq \Sigma^*

Sean L1L_1 y L2L_2 dos lenguajes:

  • Unión: L1∪L2L_1 \cup L_2 (Cadenas que están en uno u otro).
  • Concatenación: L1L2={xy∣x∈L1,y∈L2}L_1 L_2 = \{xy \mid x \in L_1, y \in L_2\}.
  • Cerradura de Kleene (L∗L^*): Repetir LL cero o más veces (incluye la cadena vacía ϵ\epsilon).
  • Cerradura Positiva (L+L^+): Repetir LL una o más veces.

Son una notación algebraica para definir lenguajes regulares (patrones).

Ejemplo: Definir un lenguaje para números (enteros o decimales, positivos o negativos).

  • Alfabeto: Dígitos D={0,…,9}D = \{0, \dots, 9\}, signo {−}\{-\}, punto {.}\{.\}.
  • Expresión: R=(−∪ϵ)D+(.D+∪ϵ)R = (- \cup \epsilon) D^+ (. D^+ \cup \epsilon)
ParteSignificado
(−∪ϵ)(- \cup \epsilon)Un guion opcional.
D+D^+Uno o más dígitos (parte entera).
(.D+∪ϵ)(. D^+ \cup \epsilon)Opcionalmente, un punto y más dígitos.

Una gramática define reglas para generar cadenas. Son la base de problemas como “Paréntesis Balanceados”.

Ejemplo: ¿Está bien formada (())()? Reglas recursivas para una cadena válida SS:

  1. S→ϵS \to \epsilon (Cadena vacía es válida).
  2. S→(S)S \to (S) (Podemos rodear una válida con paréntesis).
  3. S→SSS \to SS (Podemos concatenar dos válidas).

Un autómata es una máquina abstracta que lee una cadena y cambia de estado. Al final, decide si Acepta o Rechaza.

Queremos aceptar cadenas binarias con un número par de 1s.

  • Estados: EparE_{par} (Inicial, Aceptación), EimparE_{impar}.
  • Transiciones:
    • Si leo 0: Me quedo donde estoy.
    • Si leo 1: Cambio de estado (Par↔ImparPar \leftrightarrow Impar).

Si la cadena termina en EparE_{par}, es válida.


La teoría de autómatas y hashing es la base de los algoritmos de búsqueda eficientes (más rápidos que el .find() básico que es O(N⋅M)O(N \cdot M)).

  1. Rabin-Karp: Usa Rolling Hash (Prefix Sum de hashes) para comparar patrones en O(1)O(1).
  2. KMP (Knuth-Morris-Pratt): Construye un autómata (o arreglo de prefijos LPS) para no retroceder nunca en el texto. Complejidad O(N+M)O(N+M).