¡No me gusta el cambio!

19

Entrada:

Dos cadenas sin líneas nuevas o espacios en blanco.

Salida:

Ambas cadenas de entrada en líneas separadas, con espacios donde sea necesario para una de las dos cadenas. Y una tercera línea con los personajes A, R, My , en representación añadió , eliminó , modificado , y sin cambios .

Agregamos espacios a la cadena de entrada superior o inferior (si es necesario). El objetivo de este desafío es producir con la menor cantidad de cambios ( ARM) posible, también conocida como la distancia de Levenshtein .

Ejemplo:

Digamos que las cadenas de entrada son ABCDEFy AFBECD, entonces, la salida sería esta:

A B CDEF
AFBECD  
 A A  RR

Aquí hay algunos otros posibles resultados no válidos como ejemplo (y hay muchos más):

ABCDEF
AFBECD
 MMMMM

A BCDEF
AFBECD 
 A MMMR

AB CDEF
AFBECD 
 MAMMMR

ABC DEF
AFBECD 
 MMAMMR

ABC  DEF
AFBECD  
 MMAA RR

ABCDEF 
AFB ECD
 MMR MA

 AB CDEF   // This doesn't make much sense,
AFBECD     // but it's to show leading spaces are also allowed
AM A  RR

Sin embargo, ninguno de estos tiene solo cuatro cambios, por lo que solo A B CDEF\nAFBECD \n A A RRes una salida válida para este desafío.

Reglas de desafío:

  • Puede suponer que las cadenas de entrada no contendrán nuevas líneas o espacios.
  • Las dos cadenas de entrada pueden ser de diferentes longitudes.
  • Una de las dos cadenas de entrada debe permanecer como está, a excepción de los espacios iniciales / finales opcionales.
  • Si sus idiomas no admiten nada más que ASCII, puede asumir que la entrada solo contendrá caracteres ASCII imprimibles.
  • El formato de entrada y salida son flexibles. Puede tener tres cadenas separadas, una matriz de cadenas, una sola cadena con nuevas líneas, una matriz de caracteres 2D, etc.
  • Se le permite usar algo más en lugar de ARM, pero indique lo que ha usado (es decir 123, o abc., etc.)
  • Si es posible más de una salida válida con la misma cantidad de cambios ( ARM), puede elegir si generará una de las salidas posibles o todas.
  • Los espacios iniciales y finales son opcionales:

    A B CDEF
    AFBECD
     A A  RR
    

    o

    "A B CDEF\nAFBECD\n A A  RR"
                     ^
                     Note there are no spaces here
    

Reglas generales:

  • Este es el , por lo que la respuesta más corta en bytes gana.
    No permita que los lenguajes de código de golf lo desalienten de publicar respuestas con idiomas que no sean de código. Trate de encontrar una respuesta lo más breve posible para 'cualquier' lenguaje de programación.
  • Se aplican reglas estándar para su respuesta, por lo que puede usar STDIN / STDOUT, funciones / método con los parámetros adecuados, programas completos. Tu llamada.
  • Las lagunas predeterminadas están prohibidas.
  • Si es posible, agregue un enlace con una prueba para su código.
  • Además, agregue una explicación si es necesario.

Casos de prueba:

In: "ABCDEF" & "AFBECD"

Output (4 changes):
A B CDEF
AFBECD  
 A A  RR                  

In: "This_is_an_example_text" & "This_is_a_test_as_example"

Possible output (13 changes):
This_is_an       _example_text
This_is_a_test_as_example     
         MAAAAAAA        RRRRR

In: "AaAaABBbBBcCcCc" & "abcABCabcABC"

Possible output (10 changes):
AaAaABBbBBcCcCc
 abcABCab cABC 
R MM  MMMR MM R

In: "intf(){longr=java.util.concurrent.ThreadLocalRandom.current().nextLong(10000000000L);returnr>0?r%2:2;}" & "intf(){intr=(int)(Math.random()*10);returnr>0?r%2:2;}"

Possible output (60 changes):
intf(){longr=java.util.concurrent.ThreadLocalRandom.current().nextLong(10000000000L);returnr>0?r%2:2;}
intf(){i ntr=(      i    n      t)(M  ath.r   andom        ()*         10          );returnr>0?r%2:2;}
       MR M  MRRRRRR RRRR RRRRRR MMMRR MMMMRRR     RRRRRRRR  MRRRRRRRRR  RRRRRRRRRR 

In: "ABCDEF" & "XABCDF"

Output (2 changes):
 ABCDEF
XABCD F 
A    R 

In: "abC" & "ABC"

Output (2 changes):
abC
ABC
MM 
Kevin Cruijssen
fuente
Relacionado
Kevin Cruijssen
Si hay varios arreglos que tienen la misma distancia, ¿está bien que solo produzca uno?
AdmBorkBork
@AdmBorkBork Sí, solo una de las salidas posibles es la salida deseada (aunque también está bien enviar todas las opciones disponibles). Aclararé esto en las reglas de desafío.
Kevin Cruijssen
@Arnauld He eliminado la regla sobre los espacios iniciales, por lo que los espacios iniciales y finales son opcionales y válidos en la línea no modificada. (Lo que significa que el último caso de prueba en su respuesta es completamente válido.)
Kevin Cruijssen
1
@ Ferrybig Ah ok, gracias por la explicación. Pero en cuanto a este desafío, solo admitir ASCII imprimible ya es suficiente. Si quieres apoyar más, sé mi invitado. Pero mientras funcione para los casos de prueba dados, estoy bien con un comportamiento indefinido para los grupos de grafeno que consisten en más de 1 carácter como ese. :)
Kevin Cruijssen

Respuestas:

5

Haskell , 192 181 174 161 158 150 147 143 158 1 bytes

e@(a:r)&f@(b:s)=snd$maximum[([1|' '<-s!!2],s)|s<-z(z(:))[a:" R",' ':b:"A",a:b:last("M":[" "|a==b])][r&f,e&s,r&s]]
x&y=[x,y,"RA"!!(0^length x)<$x++y]
z=zipWith

Pruébalo en línea! Ejemplo de uso: "ABCDEF" & "AFBECD". Devuelve una lista de tres cadenas. Esta es una extensión de mi solución recursiva a la pregunta de distancia ordinaria de Levenshtein .

Explicación:

Para calcular las modificaciones mínimas para llegar "xyz"a "yw", nos centramos en el primer carácter de ambas cadenas. Hay tres posibilidades:

  • Eliminar: soltar xdesde la primera cadena y calcular recursivamente las modificaciones para llegar "yz"a "yw". Esto produce las tres líneas ["yz","yw"," M"]. Agregue xal primero, un espacio al segundo y Ral tercero. Obtenemos
    xyz
    yw
    RM
  • Agregar: soltar ydesde la segunda cadena y calcular "xyz" & "w", lo que devuelve el resultado ["xyz","w","MRR"]. Necesitamos agregar un espacio en la primera línea, ya la segunda y Aa la tercera línea:
     xyz
    yw
    AMRR
  • Modificado / Sin cambios: Podemos combinar esos dos casos, ya que ambos requieren dejar caer el primer carácter de ambas cadenas y calcular las modificaciones mínimas entre las cuerdas restantes: "yz" & "w". Al resultado ["yz","w","MR"], agregamos xel primero y yla segunda línea. Solo para la última línea necesitamos diferenciar si los caracteres iniciales son los mismos. Si son iguales, se agrega un espacio a la tercera línea, de lo contrario (como en este caso porque x \= y) Mse agrega un:
    xyz
    yw
    MMR

De esos tres candidatos, necesitamos encontrar el que tenga la menor cantidad de modificaciones. Esto es equivalente a tener la mayoría de los espacios en la tercera línea. Por lo tanto, convertimos cada candidato s(una lista de tres cadenas) en una tupla ([1|' '<-s!!2],s), donde saparece como segundo componente y el primer componente es una lista con tantos elementos como espacios en la tercera línea de s( s!!2debido a la indexación 0). Como elemento de lista 1se utiliza, pero el elemento real es irrelevante siempre que sea el mismo para todos los candidatos.

En total, esto produce la lista de tuplas

[([1], ["xyz", "yw", "RM"]), ([], ["xyz", "yw", "AMRR"]), ([], ["xyz", " yw "," MMR "])]
El incorporado maximumselecciona el elemento más grande de esta lista, donde las tuplas se comparan lexicográficamente, es decir, de componentes de izquierda a derecha. Como [1]es mayor que [], se selecciona la primera tupla y snddevuelve el segundo componente, que es la lista de líneas, de la tupla.


1 +15 bytes para corregir un error donde los Acambios al final de una cadena se mostrarían como Rcambios

Laikoni
fuente
jajaja esto hace que el script de usuario piense que esto es 1 byte
HyperNeutrino
8

JavaScript (ES6), 413 ... 343 342 bytes

Ahorró 5 bytes ajustando los índices de bucle, como lo sugiere @KevinCruijssen

Toma la entrada como 2 cadenas en la sintaxis de curry. Devuelve una matriz de 3 cadenas.

b=>a=>{m=[];x=a.length;y=b.length;for(i=j=0,c=d='';i<=y;c+=R='R')m[i]=[[c,i++]];for(;j++<x;)m[i=0][j]=[d+=A='A',j];for(;i<y;i++)for(j=0;j<x;)[C]=m[[X,S]=m[i][j],[Y,I]=m[i+1][j],[Z,D]=m[i][++j],Q=[Z+R,D+1],i+1][j]=b[i]==a[j-1]?[X+' ',S]:S<I?D<S?Q:[X+'M',S+1]:D<I?Q:[Y+A,I+1];return[(g=s=>C.replace(/./g,c=>c==s?' ':b[i++],i=0))(A),g(R,b=a),C]}

Casos de prueba

Menos golf

b => a => {
  m = []; x = a.length; y = b.length;

  // initialize the leftmost column and the topmost row
  for(i = j = 0, c = d = ''; i <= y; c += R = 'R')
    m[i] = [[c, i++]];
  for(; j++ < x;)
    m[i = 0][j] = [d += A = 'A', j];

  // walk through the Levenshtein matrix
  for(; i < y; i++)
    for(j = 0; j < x;)
      [C] = m[                                // C = current string, once updated
        [X, S] = m[i][j],                     // substitution
        [Y, I] = m[i + 1][j],                 // insertion
        [Z, D] = m[i][++j],                   // deletion
        Q = [Z + R, D + 1],                   // precomputed update for deletion
        i + 1
      ][j] =
        b[i] == a[j - 1] ?
          [X + ' ', S]                        // unchanged character
        :
          S < I ?
            D < S ? Q : [X + 'M', S + 1]      // deletion or substitution
          :
            D < I ? Q : [Y + A, I + 1];       // deletion or insertion

  return [
    // g = helper function to format the output strings by inserting spaces
    (g = s => C.replace(/./g, c => c == s ? ' ' : b[i++], i = 0))(A),
    g(R, b = a),

    // final modification string, picked from the last visited cell
    C
  ]
}

Ejemplo

A continuación se muestra la matriz inicial para b = "foo" y a = "ok" :

//                     'o'           'k'
[ [ [ '',    0 ], [ 'A',   1 ], [ 'AA',  2 ] ],
  [ [ 'R',   1 ],  (undefined),  (undefined) ],  // 'f'
  [ [ 'RR',  2 ],  (undefined),  (undefined) ],  // 'o'
  [ [ 'RRR', 3 ],  (undefined),  (undefined) ] ] // 'o'

y aquí está la matriz final después de todas las iteraciones:

//                     'o'           'k'
[ [ [ '',    0 ], [ 'A',   1 ], [ 'AA',  2 ] ],
  [ [ 'R',   1 ], [ 'M',   1 ], [ 'MA',  2 ] ],  // 'f'
  [ [ 'RR',  2 ], [ 'R ',  1 ], [ 'R A', 2 ] ],  // 'o'
  [ [ 'RRR', 3 ], [ 'RR ', 2 ], [ 'R M', 2 ] ] ] // 'o'

La cadena de modificación final junto con la distancia de Levenshtein se almacenan en la celda inferior derecha.

Arnauld
fuente
El mismo cambio que sugerí para guardar 1 byte con respecto a -1 / + 1 jy xaún se aplica a su última edición: b=>a=>{m=[];x=a.length;y=b.length+1;for(i=y;i--;)m[i]=[[i,'R'.repeat(i)]];for(j=x+1;j--;)m[i=0][j]=[j,'A'.repeat(j)];for(;++i<y;)for(j=-1;++j<x;)R=m[S=(X=m[i-1][j])[0],I=(Y=m[i][j])[0],D=(Z=m[i-1][j+1])[0],Q=[D+1,Z[1]+'R'],i][j+1]=b[i-1]==a[j]?[S,X[1]+' ']:S<I?D<S?Q:[S+1,X[1]+'M']:D<I?Q:[I+1,Y[1]+'A'];return[(g=s=>R[1].replace(/./g,c=>c==s?' ':b[i++],i=0))('A'),g('R',b=a),R[1]]}:)
Kevin Cruijssen
1
@KevinCruijssen Ahorré 5 bytes llevando tu idea un paso más allá. ¡Gracias!
Arnauld
4

Python 2 , 548 536 484 500 1 488 447 381 2 373 371 357 350 bytes

s,t=input()
n,m=len(s),len(t)
r=range
L=[[(j,'RA'[i<1]*j)for j in r(i,m-~i)]for i in r(n+1)]
for i in r(n):
 for j in r(m):M,R=L[i][j:j+2];A=L[i+1][j];v,V=min(A,R,M);x=('AR'[v in R],'M_'[s[i]==t[j]])[v in M];_=M;X=eval(x)[1]+x;L[i+1][j+1]=v+(x<'_'),X
for i in r(len(X)):s=s[:i]+' '*('B'>X[i])+s[i:];t=t[:i]+' '*('R'==X[i])+t[i:]
print s+'\n'+t+'\n'+X

Pruébalo en línea!

Usos en 'ARM_'lugar de'ARM '

Funciona construyendo la matriz de Levenshtein y luego atravesando hacia atrás para encontrar las operaciones . Ahora construye la cadena de operación al mismo tiempo que la matriz de Levenshtein, como en la respuesta JS de Arnauld

1: Más bytes, ya que no funcionó si la primera cadena era un solo carácter.

2: Cambió a construir las combinaciones en la matriz de Levenshtein.

TFeld
fuente
+1 por tomar menos de 60 segundos para palabras de 6 caracteres como mi intento inicial (fallido) jajaja
HyperNeutrino
¡Buena respuesta! +1 de mi parte Como nunca programo en Python, realmente no puedo ayudarte con el golf, excepto por una cosa: m+i+1puede ser m-~i.
Kevin Cruijssen
Puede usar una pestaña en lugar de espacios dobles en la línea 7.
Stephen
Puede obtener 463 bytes reduciendo el bucle while i+j<n+m:v,A=(L[i]+[m,m])[j:j+2];R,M=(L[i+1]+[m,m])[j:j+2];d=min(A,R,M);q=M==d or(R==d)*2;r+=' '*(d==v==M)or'AMR'[q];i+=q>0;j+=q<2
wile
1

Python 2 , 310 bytes

from difflib import*
a,b=input()
U,j=[],' '
for g,s,t,x,y in SequenceMatcher(0,a,b).get_opcodes():
	g,Y,T=g[0],y-x,t-s
	z,A,B=Y-T,a[s:t],b[x:y]
	if'q'<g:U+=[A+z*j,B+j*-z,'M'*min(T,Y)+'A'*z+'R'*-z]
	if'e'==g:U+=[A,B,j*Y]
	if'i'==g:U+=[j*Y,B,'A'*Y]
	if'e'>g:U+=[A,j*T,'R'*T]
for e in[0,1,2]:print''.join(U[e::3])

Pruébalo en línea!

Usar difflib.SequenceMatchereso calcula una alineación entre dos cadenas

mdahmoune
fuente
Esto parece dar algunos resultados incorrectos para algunos de los otros casos de prueba. Más particular:"This_is_an_example_text","This_is_a_test_as_example"
Kevin Cruijssen
@KevinCruijssen gracias, lo acabo de arreglar ^ _ ^
mdahmoune
Bien, gj! Pero umm ... el tercer caso de prueba (y el cuarto también) también es incorrecto desafortunadamente. Una de las dos líneas no debe modificarse (excepto los espacios iniciales / finales). Ambas líneas actualmente contienen espacios en el medio.
Kevin Cruijssen
@KevinCruijssen gracias de nuevo, lo estoy arreglando
mdahmoune
1

Mathematica, 250 256 259 384 bytes

~ 0.00035 segundos para el caso del código java.

(i=o=p="";a=Array;r=StringLength;If[Length@#>0,g=#&@@#;h=#[[2]];u=r@g;v=r@h;i=i<>g;o=o<>h;w=Abs[v-u];k=a[" "&,w];If[u<v,i=i<>k,o=o<>k];p=p<>a["M"&,u~Min~v];p=p<>a[If[u>v,"R","A"]&,w],i=i<>#;o=o<>#;p=p<>a[" "&,r@#]]&/@SequenceAlignment[#,#2];{i,o,p})&

Uso: f["ABCDE", "ABCDF"]

Pruébalo en línea!

El código se basa en un hecho que SequenceAlignmentfunciona de forma predeterminada en

Con la configuración predeterminada SimilarityRules-> Automatic, cada coincidencia entre dos elementos contribuye 1 al puntaje de similitud total, mientras que cada falta de coincidencia, inserción o eliminación contribuye -1.

A saber, la puntuación se calcula por M, Ay R, en consecuencia.

Ejemplo:

ejemplo

Keyu Gan
fuente
2
Hmm, nunca he programado en Mathemetica, pero ¿no es posible cambiar i="";o="";p="";a i="";o=i;p=i;reducir 2 bytes?
Kevin Cruijssen
2
¿Qué hay de i=o=p=""?
DavidC
@DavidC Sí, me di cuenta de eso y ya lo cambié. gracias de todos modos
Keyu Gan
1

D , 351345 bytes

-6 bytes gracias a KevinCruijssen

string f(string a,string b){import std.algorithm;string x,y,z;size_t i,j,k;foreach(c;levenshteinDistanceAndPath(a,b)[1]){final switch(c)with(EditOp){case none:x~=a[i++];y~=b[j++];z~=" ";break;case substitute:x~=a[i++];y~=b[j++];z~="M";break;case insert:x~=" ";y~=b[j++];z~="A";break;case remove:x~=a[i++];y~=" ";z~="R";}}return x~"\n"~y~"\n"~z;}

Pruébalo en línea!

mdahmoune
fuente
Puede jugar al golf seis bytes quitando el último break;. +1, sin embargo, la primera vez que veo el lenguaje de programación D.
Kevin Cruijssen