Antes que nada, debes decidir/indicar, si es admisible que se revisite un nodo dos ó x veces, ó solo una como máximo... sino, por ejemplo sería valido la ruta:
A->C, B, D, H, F, C, L ...Que, como se puede ver, revisita el nodo C dos veces...
Una vez decidido, y dando por supuesto que no se admitite revisitar nodos, puede tratarse como un automata finito determinista (AFD), donde el alfabeto es el conjunto de nodos (A, B, C, D, F, H, L) donde el estado inicial es A y el estado final es L, entonces puede establecerse el resto de nodos como estados de transición entre uno y otro.
Así desde A solo puede accederse a F y a C, lo que por ejemplo tratado como un autómata finito determinista , se señalaría en BNF como:
A = C|F
y desde C, se puede acceder a los nodos
C = A|F|B|L
El resto d eproducciones:
F = A|H
H = F|D
D = H|B
B = C|D
L = C <--- esta no interesa, se trata de llegar a L, no pasar po él (es el estado final, no un estadode transición)
Como se puede ver, las producciones BNF, son las adyacencias de un nodo, la cantidad de nodos a los que se accede (por ejemplo A|F|B|L) son las aristas que salen de dicho nodo (en el ejemplo del nodo C, en C = A|F|B|L).
Entonces una vez en el estado inicial (A, pongamos estado 1), solo puede transitar hacia un estado 2, y desde un estado 2 a otro estado 2 o al estado de transición 3, si no tiene otro nodo que sea visitable que uno ya visitado, va al estado de error y por tanto ese camino no tiene solución, debe volverse un paso atrás cada vez que esto suceda... el estado de transición 3 (L), es el estado final, y señal de que admite ese 'token', para la gramática del alfabeto propuesto...
Puede tratarse como un automata de pila, porque un nodo no puede visitarse más de una vez...
A diferencia claro, de por ejemplo cuando se hace un compilador que en la entrada viene un texto y se trata de ver si un token pertenerce o no a la gramática, en tu caso haces algo distinto, la entrada es nula y tratas de averguar todas las salidas válidas (para una gramática libre de contexto serían incontables), pero para el caso de un grafo como el del ejemplo, es finito... ...de entrada se sabe que como máximo la longitud de palabra es la del alfabeto (ó menor)...
Por cuestiones de rendimiento, la pila realmente puede ser remplazada por un array de bytes donde cada letra del alfabeto tenga un valor true (no visitado ún, esto es, es Visitable), y una vez usado (pop si fuera una pila) su valor se ponga a false (nodo visitado, no visitable ya)... la velocidad de usar el array sobre una pila, está en el acceso aleatorio. al no exigir tener que extraer lo de encima de la pila... para acceder a un nodo.
Aquí un pseudocódigo casi completo... la función donde ba todo el trabajo lo dejo a tu esfuerzo...
funcion TodasLasRutasDeUnGrafo(string alfabeto) // sin repetir nodo.
array bytes pila() // realmente no es una pila, pero acomoda llamarlo así.
array string prod= GetProducciones //las producciones BNF, como un array de strings.
// Albabeto = ABCDFHL la primera letra debe ser el estado inicial
Bucle k desde 2letra de alfabeto hasta última-1 //y la última debe ser el estado final.
x = alfabeto(k) //letra enésima del alfabeto
c= caracter kesimo en alfabeto
Hacer
pila = LlenarPila(alfabeto) //vuelve visitable todos sus nodos.
//pila(ASCII(c)) = FALSE // excepto la letra inicial de la que partimos esta vez,
// pero esto mejor se traslada a la función invocada.
token = SiguienteToken(prod, pila, c)
Si token <> ""
imprimir "A --> " + token
sino
salir del bucle interno
fin si
Repetir //mientras token distinto de nulo
Siguiente
// el array es fijo según el grafo, y tal como se indico en las producciones.
// al caso se 'montan' en un array de strings,
array de String = Funcion GetProducciones
array de string p(0 a 255)
//Cada valor está en la casilla cuyo ASCII representa y su contenido son la representación de cada nodo al que se accede desde él,
// cada letra representa un nodo, al caso se han ordenado alfabéticamente, pero puede seguirse el orden en que aparecen sobre el grafo girando en uno u otro sentido, no importa...
p(ASCII(A)) = CF
p(ASCII(B)) = CD
p(ASCII(C)) = ABLF
p(ASCII(D)) = BH
p(ASCII(F)) = AH
p(ASCII(H)) = DF
//p(ASCII(L))
devolver p
fin funcion
array de bytes = funcion LlenarPila(string alfabeto)
array de bytes x(0 a 255)
byte k
Por cada letra en Alfabeto
x(ASCII(letra)) = TRUE //por ejemplo si letra es A: x(65) = TRUE, porque A es el carácter ASCII 65
siguiente
devolver X
fin funcion
Y la función final que lleva todo el trabajo, la dejo a tu esfuerzo... pero con algunas anotaciones.
enumeracion Estados
ESTADO_ERROR = 0
ESTADO_INICIAL = 1
ESTADO_TRANSICION = 2
ESTADO_FINAL = 3
fin enumeracion
// de entrada sabemos que el estado inicial es 1, y el primer carácter-nodo es A, que puede obviarse.
string = funcion SiguienteToken(array string P(), array bytes Pila(), char letra)
Estados e= ESTADO_INICIAL
string t = letra // la letra inicial, para esta vez.
letra = siguiente letra en p(ASCII(letra)) // esto exige recursión ya que una producción engloba a otra.
// Si pila(ASCII(letra)) = TRUE si el nodo de dicha letra es visitable
// t += letra
// Si letra = "L"
// e = ESTADO_FINAL
// devolver t
// sino
// e = ESTADO_TRANSICION
// marcar letra no visitable pila(ASCII(letra)) = False
// fin si
// sino
// volver atras, si es recursivo, implica devolver
// fin si
// Si un nodo no es visitable, volver atrás, y si ya no quedan más nodos hacia adelante devolver false.
fin funcion
Finalmente decirte que casi siempre verás una solución con un cuerpo más matemático, tirando de árboles... , la solución (incompleta) que te aporto, es... no más cercana, sino íntima a la programación... pués entra en la parte de la teoría de compiladores. Aunque al caso ha habido una discreta variación... a medias entre un análisis léxico y sintáctico.
Otra solución es recurrir a la combinatoria... generar (ir generando y probando sobre la marcha cada una si es un camino, esto es si el siguiente nodo es accesible desde el actual) todas las permutaciones sin repetición de largo máximo el alfabeto
ABCDFHL (menos la letra inicial).
Como algoritmo es mucho más simple, pero es más lento en ejecución, especialmente si el alfabeto fuera mucho más largo, para este cortito, todavía no es significativo.
string permutacion, ruta
array de string p() // el array de producciones que se generó el en otro pseudocódigo... traer la función aquí.
por cada permutacion sobre el alfabeto //BCDFHL, nota que retiramos la A, pués siempre es el nodo inicial
ruta = Substring(permutacion, hasta "L") // obtenemos la subcadena hasta L, lo que tenga detrás sobra
si EsCamino(ruta, p ) = TRUE
imprimir "A" + ruta
fin si
siguiente
buleano = EsCamino(string ruta, array strings p) // p son el array de las producciones generadas en el otro pseudocódigo
char c = ruta(0)
char n
//... recorrer todo el string, viendo si desde la letra actual, hay conexión al otro nodo (siguiente letra)
bucle para k desde 1 hasta largo de ruta
n = ruta(k)
si p(ASCII(c)) contiene n
c = n
sino
devolver false
fin si
siguiente
devolver TRUE // c = "L"
fin funcion
// OJO: Alfabeto aquí deja fuera la letra A, no queremos que cambie de posición porque sabemos que siempre ha de ser el primero, no cabe en otra posición.
// alfabeto = "BCDFHL"
string = SiguientePermutacion(string alfabeto , string permutacion)
... calcula y devuelve la siguiente permutacion a la recibida. inicialmente permutacion = alfabeto
fin funcion