Mi amigo me dio un problema que dice que es fácil, pero no puedo encontrar un buen algoritmo para hacerlo.
Te dan una entrada de 100 palabras aleatorias en inglés. Tienes que encontrar la cadena de palabras más larga donde la última letra de una palabra coincide con la primera letra de la siguiente palabra. Solo puedes usar cada palabra una vez.
Por ejemplo, si le dieran las palabras "gato", "perro", "eso", la cadena más larga que podría hacer sería "gato -> eso". Si le dieran las palabras "mouse", "alce", "unicornio", la cadena más larga que podría hacer sería una sola palabra (ya que ninguna de esas palabras se vincula). Si le dieran las palabras "pájaro", "plato", "harb", la cadena más larga que podría hacer sería "harb -> bird -> dish" (o "dish -> harb -> bird" o "bird - > plato -> harb ").
Se me ocurrió la idea de modelar esto como un gráfico cíclico dirigido. Cada nodo sería solo una palabra, con vértices que van a cada palabra / nodo que comenzó con la letra con la que terminó esta palabra.
+-------+ \ +------+
| cat |-----------| that |
+-------+ / +------+
| |
\|/ |
+-------+ / |
| the |--------------+
+-------+ \
Este problema parece ser una búsqueda de ruta más larga , que es NP-Hard.
Hay una mejor manera de hacerlo? ¿O incluso algún tipo de algoritmo de aproximación que podría usarse? ¿O alguna forma de explotar las cualidades del inglés para reducir el espacio de búsqueda?
fuente
Respuestas:
Creo que esto está relacionado con el problema de la ruta más larga (LP) que mencionaste, pero es un poco diferente. La principal diferencia es que el problema de LP tiene un mayor grado de conectividad que el problema sugerido. Al restringir sus conexiones a la última y primera letra, elimina una gran cantidad de combinaciones potenciales.
Así es como recomendaría abordar este:
next word
, repita el paso 5 hasta que la cadena termine.Manten eso en mente:
Deberá realizar un seguimiento de la longitud de las cadenas y tener algún mecanismo global para identificar la cadena más larga.
También deberá eliminar cada palabra de la copia de trabajo de los recuentos de conexiones para evitar un bucle recursivo.
En algún momento, su cadena terminará y tendrá que seleccionar una palabra con un recuento de conexiones 0 fuera.
Puede que tenga que volver a calcular entradas / salidas a medida que las palabras se eliminan de las listas de trabajo. A primera vista, no creo que esto sea necesario ya que los conjuntos generales serán relativamente pequeños. Si escalaste a 1000 palabras, tener conteos estáticos puede ralentizar la convergencia del algoritmo.
En cierto modo, vi esto como un problema de embalaje. Para mí, las conexiones dentro y fuera identifican la forma que se va a empacar. Cuanto más bajas son las conexiones, más extraña es la forma. Cuanto más extraña es la forma, más pronto quiero empacarla ya que percibo que hay menos probabilidades de poder empacar una forma extraña cuanto más tarde me meto en la cadena.
Como ejemplo:
fuente
Si realiza una matriz 26X26 para representar el gráfico dirigido de vértice como cada alfabeto y las palabras como borde. Por ejemplo, la palabra: APPLE conecta el vértice A y E con el borde dirigido de A a E. Ahora el problema se reduce a encontrar el rastro euleriano más grande (ruta que incluye el número máximo de bordes, visitando cada borde una vez con la posible repetición de vértices) en el gráfico. Uno de los algoritmos O (E) sería comenzar aleatoriamente desde un par de vértices. Encuentra un camino entre ellos. Que seguir relajando el camino hasta que sea posible.
update @ GlenH7 Resolví una pregunta similar en www.hackerearth / jda recientemente, hubo marcas relativas con respecto a la mejor solución y obtuve las calificaciones más altas con el siguiente enfoque:
Lista de palabras dada. Encuentra la cadena más larga que puedan formar. Una cadena es válida si cada palabra comienza con una letra * que termina al final de la última palabra.
Aproximación =
1) haz la gráfica de los alfabetos como vértices y las palabras como bordes. En lugar de usar varios bordes, use uno con un peso igual al número de bordes.
2) encuentre el componente fuertemente conectado del gráfico con aristas máximas. Deseche temporalmente otros bordes.
3) Para cada vértice, haga que su grado de entrada sea igual a su grado de salida.
4) Ahora existe su circuito euleriano en gráfico. Encuéntralo.
5) Ahora en el gráfico restante (gráfico original de wrt, encuentre el camino más largo con el primer vértice en el componente fuertemente conectado elegido. Creo que esto es NP difícil.
6) Incluya la ruta anterior en el circuito Eleriano convirtiendo el circuito euleriano en ruta.
Por qué: acepto que esta pregunta es probablemente NP difícil (supongo, no matemáticamente hablando). Pero el enfoque anterior funciona mejor cuando hay una larga lista (más de 1000) de palabras distribuidas uniformemente (es decir, no está destinado a ser wc para el enfoque anterior). Supongamos que después de convertir la lista dada al gráfico mencionado anteriormente, afortunadamente resulta ser un gráfico euleriano (consulte http://en.wikipedia.org/wiki/Eulerian_path para conocer las condiciones), entonces sin ninguna duda podemos decir esa respuesta la pregunta anterior es P y en realidad es la ruta euleriana en el gráfico (consulte http://www.graph-magics.com/articles/euler.php para obtener una aproximación muy simple y verifique que su gráfico tenga solo http://www.geeksforgeeks.org/strongly-connected-components/y si no se limpia temporalmente otro scc pequeño porque existe una ruta euleriana para scc único). Por lo tanto, para los casos no afortunados (que son casi todos los casos) trato de convertirlos en casos afortunados (es decir, se cumplen las condiciones del rastro euleriano). ¿Como hacer esto? Intenté hacer una búsqueda de profundidad creciente para bordes irrelevantes (el conjunto de bordes en una ruta que mira desde el vértice con un grado mayor que indegrado y que termina en el vértice con un grado mayor que excedido). La búsqueda de profundidad creciente significa que primero busqué todo el conjunto de un borde en la ruta que dos bordes en la ruta y así sucesivamente. A primera vista puede parecer que la búsqueda de profundidad i-ésima tomaría O (nodos ^ i), por lo tanto, la complejidad del tiempo total de O (nodos + nodos ^ 2 + nodos ^ 3 + ....) hasta que sea un caso afortunado. Pero el análisis amortizado lo deleitará es O (bordes). Una vez que se reduce el caso de la suerte, busca el circuito euleriano
Hasta aquí todo era tiempo polinómico. Esto daría casi la mejor solución. Pero para aumentar aún más su solución (la solución perfecta es NP difícil) intente un enfoque codicioso en el gráfico restante para encontrar un camino largo mirando con uno de los vértices en el scc elegido. Ahora agregue esto al rastro euleriano encontrado anteriormente para aumentarlo aún más.
fuente
Idea:
Primero, cree dos mapas (hashes), digamos, S y E, desde letras del alfabeto hasta palabras; el primero, S, asigna letras iniciales a palabras, el segundo, E, hace lo mismo con letras finales.
Por ejemplo, si el diccionario está hecho de:
pájaro, plato, perro, harb
tenemos:
y,
Luego, usando S y E para búsquedas rápidas, cree un bosque (conjunto de árboles), del mismo tamaño que el diccionario, con raíces en cada palabra, y que no permita que una palabra aparezca más de una vez en un árbol: guarde en caché profundidades de los árboles a medida que los construyes:
Finalmente, repita sobre el bosque y encuentre los árboles de mayor profundidad.
La (s) solución (es) estará en el eje descendente de esos árboles.
P.ej,
encima.
fuente