Python 3, 423 bytes
import sys,re
S=re.sub
D,*L=sys.stdin.read().split('\n')
def f(W,M=[],V="",r=0):
if len({d for(s,d)in M})==len(M):
if[]==W:return V.lower()
for d in D.split():p='([a-z])(?%s.*\\1)';m=re.match(S(p%'=',')\\1=P?(',S(p%'!',').>\\1<P?(',W[0].translate(dict(M))[::-1]))[::-1]+'$',d.upper());r=r or m and f(W[1:],M+[(ord(s),m.group(s))for s in m.groupdict()],V+d+" ")
return r
for l in L:print(f(l.split())or S('\w','*',l))
Lee la entrada de STDIN y escribe la salida en STDOUT, utilizando el mismo formato que la entrada / salida de muestra.
Explicación
Para cada línea de texto cifrado, realizamos el siguiente procedimiento:
Mantenemos un mapa, M , de todas las transformaciones de letras que ya hemos establecido (que inicialmente está vacío). Lo hacemos de tal manera que las letras de origen están en minúsculas y las letras de destino están en mayúsculas.
Procesamos las palabras en el texto cifrado en orden. Para cada palabra, encontramos todas las palabras en el diccionario que podrían coincidir, de la siguiente manera:
Supongamos que nuestra palabra, w , es glpplppljjl
y que M contiene la regla j -> P
. Primero transformamos w usando las reglas existentes en M , obteniendo glpplpplPPl
. Luego transformamos w en la siguiente expresión regular con sabor a pitón:
(?P<g>.)(?P<l>.)(?P<p>.)(?P=p)(?P=l)(?P=p)(?P=p)(?P=l)PP(?P=l)
Las reglas de la transformación son las siguientes:
- La primera aparición de cada letra minúscula
x
, se reemplaza con . Esto define un grupo de captura con nombre, llamado , que coincide con un solo carácter.(?P<
x
>.)
x
- Cada aparición posterior, cada letra minúscula
x
, se reemplaza con . Esta es una referencia al personaje previamente capturado por el grupo nombrado .(?P=
x
)
x
Realizamos esta transformación invirtiendo w , luego aplicando las dos siguientes sustituciones de expresiones regulares:
s/([a-z])(?!.*\1)/)>\1<P?(/
s/([a-z])(?=.*\1)/)\1=P?(/
y luego invirtiendo el resultado. Tenga en cuenta que los caracteres previamente transformados por M aparecen en mayúsculas y, por lo tanto, permanecen sin cambios.
Hacemos coincidir la expresión regular resultante con cada una de las palabras del diccionario, donde las palabras del diccionario aparecen en mayúsculas. Por ejemplo, la expresión regular anterior coincidiría con la palabra MISSISSIPPI
. Si nos encontramos con un partido, extraemos las nuevas reglas de transformación de ella, y añadirlos a M . Las nuevas reglas de transformación son simplemente los personajes capturados por cada uno de los grupos de captura. En la expresión regular anterior, el grupo g
coincide M
, el grupo l
coincide I
y el grupo p
coincide S
, dándonos las reglas g -> M, l -> I, p -> S
. Tenemos que asegurarnos de que las reglas resultantes sean consistentes, es decir, que no haya dos letras de origen asignadas a la misma letra de destino; de lo contrario, rechazamos el partido.
Luego procedemos a la siguiente palabra, usando las reglas de transformación aumentada. Si podemos unir todas las palabras de texto cifrado utilizando este proceso, hemos descifrado el texto. Si no podemos hacer coincidir una palabra con ninguna de las palabras del diccionario, retrocedemos e intentamos hacer coincidir las palabras anteriores con diferentes palabras del diccionario. Si este proceso falla, no hay solución, e imprimimos una fila de asteriscos.