Problema y Contexto
Dado un flujo s[0..n), se añade un carácter a la vez. Después de cada append, mantén el número de subcadenas palindrómicas distintas, los conteos de ocurrencias para cada palíndromo y el sufijo palindrómico más largo del prefijo actual. La solución debe ser en línea en lugar de volver a enumerar todas las subcadenas después de cada append.
Un eertree (árbol palindrómico) almacena un nodo por cada palíndromo distinto. Las aristas añaden el mismo carácter a ambos extremos, mientras que un enlace de sufijo apunta al sufijo palindrómico propio más largo. Una respuesta sólida explica las dos raíces centinela, cómo se encuentra un sufijo extensible y por qué se crea como máximo un nodo por cada posición.
Qué Evalúa el Entrevistador
- Distinguir correctamente las raíces de longitud
-1y longitud0. - Comprender
last, el sufijo palindrómico más largo y los enlaces de sufijo. - Encontrar un nodo extensible y crear una transición durante el append.
- Conocer los límites de nodos, tiempo y espacio
O(n)bajo el modelo de append. - Manejar caracteres repetidos, la cadena vacía, la representación del alfabeto y la propagación de conteos.
- Extender la estructura a particiones de palíndromos o variantes de ventana deslizante.
Aclaraciones a Preguntar Primero
- ¿La entrada es una cadena única o un flujo donde solo se agrega a la derecha? ¿Se debe eliminar el lado izquierdo?
- ¿Las ocurrencias deben contarse por posición final o como frecuencias totales definitivas?
- ¿El alfabeto es de minúsculas, Unicode o tokens enteros arbitrarios?
- ¿La salida debe contener el texto del palíndromo, un id de nodo o solo longitud y conteos?
- ¿Se requieren cortes mínimos en línea o basta con mantener el conjunto de palíndromos distintos?
Marco de Respuesta en 30 Segundos
Uso dos raíces: longitud -1 y longitud 0. Cada nodo ordinario almacena su longitud de palíndromo, un enlace de sufijo al sufijo palindrómico propio más largo y transiciones de caracteres. last es el sufijo palindrómico más largo del prefijo actual. Cuando llega el carácter c, sigo los enlaces de sufijo hasta que ambos lados puedan ser envueltos por c; reutilizo una transición existente o creo una, y luego calculo el enlace de sufijo del nuevo nodo a partir de la cadena de enlaces. Como máximo se puede añadir un nodo distinto por posición, por lo que la construcción es O(n), y propagar los conteos de ocurrencias en orden inverso de enlaces de sufijo proporciona las frecuencias finales.
Análisis Detallado Paso a Paso
1. Dos Raíces y Campos de los Nodos
La raíz impar tiene longitud -1, actuando como un centinela que puede extenderse con cualquier carácter. La raíz par tiene longitud 0 y representa el palíndromo vacío. Los nodos ordinarios almacenan len, link, next, occ y opcionalmente una posición final. last comienza en la raíz par.
2. Encontrar un Sufijo Extensible
Después de añadir c en la posición pos, comienza desde last y comprueba si el carácter justo antes del palíndromo del nodo es igual a c. Si no es así, asigna v = link[v] y continúa. La primera coincidencia es el sufijo palindrómico más largo que se puede extender.
while s[pos - 1 - len[v]] != c:
v = link[v]Las implementaciones comúnmente anteponen un centinela fuera del alfabeto para que la comprobación de la raíz impar nunca lea un índice negativo.
3. Añadir una Transición y un Nodo
Si next[v][c] ya existe, se convierte en el nuevo last y su occ se incrementa. De lo contrario, crea un nodo de longitud len[v] + 2 y asigna la transición. Un nodo de longitud uno se enlaza directamente a la raíz par. Para nodos más largos, sigue link[v] hasta encontrar la transición correspondiente en c.
4. Por Qué Solo Se Añade un Nodo
Cada palíndromo creado de nuevo por un append debe terminar en el nuevo carácter. Solo el palíndromo más largo de este tipo es nuevo; sus sufijos palindrómicos más cortos ya están en la cadena de enlaces de sufijo. Por lo tanto, cada posición crea a lo sumo un nodo distinto, manteniendo el total en como máximo n + 2.
5. Propagar Conteos de Ocurrencias
Durante el paso en línea, incrementa occ para el sufijo palindrómico más largo que termina en cada posición. Una vez finalizada la entrada, procesa los nodos de mayor a menor longitud y añade occ[v] a occ[link[v]]. Esto transfiere cada ocurrencia a todos sus sufijos palindrómicos. Si solo se requiere el conteo de distintos, devuelve el número de nodos ordinarios.
6. Extender a Partición de Palíndromos
Para cortes mínimos de palíndromos, enumera los palíndromos que terminan en cada posición recorriendo la cadena de enlaces de sufijo last y actualiza dp[pos] = min(dp[pos - len[v]] + 1). Un recorrido ingenuo de la cadena puede convertirse en O(n^2). Los enlaces de serie pueden agrupar tramos con igual diferencia de longitud, pero la optimización debe elegirse solo después de confirmar las restricciones.
7. Casos Límite, Alfabeto y Complejidad
La entrada vacía solo tiene las dos raíces. Los caracteres repetidos reutilizan transiciones y no deben crear nodos duplicados. Un alfabeto pequeño puede usar arreglos fijos con almacenamiento de transiciones de O(n * alphabet); un alfabeto grande necesita un hash map o un mapa ordenado, lo que da un comportamiento esperado de O(n) u O(n log σ). Bajo inserciones solo hacia la derecha, la construcción es O(n) con transiciones hash de tiempo constante esperado, y el espacio es O(n) más el almacenamiento de transiciones.
Respuesta Modelo de Alta Calidad
Primero confirmaría las inserciones solo hacia la derecha, el alfabeto y qué significa un conteo de ocurrencias. La estructura tiene raíces de longitud -1 y 0; los nodos ordinarios representan palíndromos distintos, y last es el sufijo palindrómico más largo del prefijo actual. Para cada c añadido, sigo los enlaces de sufijo hasta el nodo más largo que pueda envolverse con c. Si su transición está ausente, creo un nodo de longitud len + 2; un nodo de longitud uno se enlaza a la raíz par, mientras que los nodos más largos encuentran su enlace a través de la cadena de enlaces de sufijo del padre. Se crea como máximo un nodo por posición, por lo que la construcción es lineal. Registrar cada last y propagar los conteos de los nodos más largos a sus enlaces produce las frecuencias totales. La eliminación por la izquierda, la inserción arbitraria o un alfabeto grande requieren reconsiderar la estructura y la complejidad.
Errores Comunes
- Usar una sola raíz vacía → los límites pares e impares se vuelven complicados → mantén ambas raíces
-1y0. - Reiniciar desde una raíz en cada append → pierde la propiedad lineal en línea → sigue los enlaces de sufijo desde
last. - Tratar a
lastcomo el palíndromo más largo en cualquier parte → es solo el sufijo palindrómico más largo. - Enlazar un nuevo nodo a su padre → el enlace debe apuntar al sufijo palindrómico propio más largo.
- Incrementar cada palíndromo en cada append → duplica conteos → registra los nodos finales y propaga en orden inverso de enlaces.
- Aplicar un arreglo fijo diminuto a Unicode arbitrario → colisiones o desbordamiento → define la codificación y el mapeo explícitamente.
Preguntas de Seguimiento y Respuestas
¿Cuándo elegirías Manacher en su lugar?
Manacher es una buena opción para una cadena estática cuando la única necesidad es el radio más largo en cada centro. Un eertree representa cada palíndromo distinto y soporta de forma natural inserciones en línea, conteos a nivel de nodo y consultas de enlaces de sufijo.
¿Cómo devuelves el texto del palíndromo más largo actual?
Almacena una posición final en cada nodo. El par de esa posición y len identifica un segmento en la entrada conservada. Un flujo que descarta la entrada necesita un búfer circular o almacenamiento externo.
¿Por qué propagar los conteos en orden inverso?
Cada ocurrencia de un palíndromo más largo es también una ocurrencia de cada sufijo palindrómico en su ruta de enlaces. Procesar primero los nodos más largos asegura que la contribución de cada hijo esté completa antes de sumarse a su padre.
¿Puede la estructura eliminar desde la izquierda?
El eertree ordinario solo admite adiciones por la derecha. Una ventana deslizante necesita una variante de doble extremo o reconstrucción/bloques; la elección depende del tamaño de la ventana y la tasa de eliminación.
¿Qué cambia con las transiciones basadas en hash-map?
Los hash maps proporcionan una búsqueda de transiciones de O(1) esperado y una construcción de O(n) esperado. El comportamiento en el peor de los casos depende de la implementación del hash. Los mapas ordenados proporcionan límites deterministas con un factor O(log σ).