Distancia Levenshtein


39

Si bien hay muchas preguntas de distancia de edición, como esta , no hay una pregunta simple para escribir un programa que calcule la distancia de Levenshtein.

Alguna exposición

La distancia de edición de Levenshtein entre dos cadenas es el número mínimo posible de inserciones, eliminaciones o sustituciones para convertir una palabra en otra. En este caso, cada inserción, eliminación y sustitución tiene un costo de 1.

Por ejemplo, la distancia entre rolly rollinges 3, porque las eliminaciones cuestan 1, y necesitamos eliminar 3 caracteres. La distancia entre tolly talles 1, porque las sustituciones cuestan 1.

Reglas

  • La entrada será de dos cadenas. Puede suponer que las cadenas son minúsculas, solo contienen letras, no están vacías y tienen un máximo de 100 caracteres de longitud.
  • La salida será la distancia mínima de edición de Levenshtein de las dos cadenas, como se definió anteriormente.
  • Su código debe ser un programa o una función. No necesita ser una función con nombre, pero no puede ser una función integrada que calcule directamente la distancia de Levenshtein. Se permiten otros complementos.
  • Este es el código de golf, por lo que gana la respuesta más corta.

Algunos ejemplos

>>> lev("atoll", "bowl")
3
>>> lev("tar", "tarp")
1
>>> lev("turing", "tarpit")
4
>>> lev("antidisestablishmentarianism", "bulb")
27

Como siempre, si el problema no está claro, hágamelo saber. ¡Buena suerte y buen golf!

Catalogar

var QUESTION_ID=67474;var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe";var COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk";var OVERRIDE_USER=47581;var answers=[],answers_hash,answer_ids,answer_page=1,more_answers=true,comment_page;function answersUrl(index){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+index+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(index,answers){return"http://api.stackexchange.com/2.2/answers/"+answers.join(';')+"/comments?page="+index+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:true,success:function(data){answers.push.apply(answers,data.items);answers_hash=[];answer_ids=[];data.items.forEach(function(a){a.comments=[];var id=+a.share_link.match(/\d+/);answer_ids.push(id);answers_hash[id]=a});if(!data.has_more)more_answers=false;comment_page=1;getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:true,success:function(data){data.items.forEach(function(c){if(c.owner.user_id===OVERRIDE_USER)answers_hash[c.post_id].comments.push(c)});if(data.has_more)getComments();else if(more_answers)getAnswers();else process()}})}getAnswers();var SCORE_REG=/<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/;var OVERRIDE_REG=/^Override\s*header:\s*/i;function getAuthorName(a){return a.owner.display_name}function process(){var valid=[];answers.forEach(function(a){var body=a.body;a.comments.forEach(function(c){if(OVERRIDE_REG.test(c.body))body='<h1>'+c.body.replace(OVERRIDE_REG,'')+'</h1>'});var match=body.match(SCORE_REG);if(match)valid.push({user:getAuthorName(a),size:+match[2],language:match[1],link:a.share_link,});else console.log(body)});valid.sort(function(a,b){var aB=a.size,bB=b.size;return aB-bB});var languages={};var place=1;var lastSize=null;var lastPlace=1;valid.forEach(function(a){if(a.size!=lastSize)lastPlace=place;lastSize=a.size;++place;var answer=jQuery("#answer-template").html();answer=answer.replace("{{PLACE}}",lastPlace+".").replace("{{NAME}}",a.user).replace("{{LANGUAGE}}",a.language).replace("{{SIZE}}",a.size).replace("{{LINK}}",a.link);answer=jQuery(answer);jQuery("#answers").append(answer);var lang=a.language;lang=jQuery('<a>'+lang+'</a>').text();languages[lang]=languages[lang]||{lang:a.language,lang_raw:lang.toLowerCase(),user:a.user,size:a.size,link:a.link}});var langs=[];for(var lang in languages)if(languages.hasOwnProperty(lang))langs.push(languages[lang]);langs.sort(function(a,b){if(a.lang_raw>b.lang_raw)return 1;if(a.lang_raw<b.lang_raw)return-1;return 0});for(var i=0;i<langs.length;++i){var language=jQuery("#language-template").html();var lang=langs[i];language=language.replace("{{LANGUAGE}}",lang.lang).replace("{{NAME}}",lang.user).replace("{{SIZE}}",lang.size).replace("{{LINK}}",lang.link);language=jQuery(language);jQuery("#languages").append(language)}}
body{text-align:left!important}#answer-list{padding:10px;width:290px;float:left}#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table>

Respuestas:


8

Pyth, 34 bytes

J]wf}z=Jsmsm++.DdkXLdkGXLkdGhld-Jk

Demostración

Esto no es particularmente bueno, y es muy lento. No puede manejar nada más allá de 2 cambios en un período de tiempo razonable.


3
Pero funciona, y eso es lo que cuenta. : P
Conor O'Brien

10

Matlab, 177 163 bytes

function l=c(a,b);m=nnz(a)+1;n=nnz(b)+1;for i=0:m-1;for j=0:n-1;z=max(i,j);try;z=min([l(i,j+1)+1,l(i+1,j)+1,l(i,j)+(a(i)~=b(j))]);end;l(i+1,j+1)=z;end;end;l=l(m,n)

Esta es una implementación sencilla de esta fórmula:

ingrese la descripción de la imagen aquí

Sin golf:

function l=l(a,b);
m=nnz(a)+1;
n=nnz(b)+1;
for i=0:m-1;
    for j=0:n-1;
        z=max(i,j);
        try;
            z=min([l(i,j+1)+1,l(i+1,j)+1,l(i,j)+(a(i)~=b(j))]);
        end;
        l(i+1,j+1)=z;
    end;
end;
l=l(m,n)

Si el código calificado no es lo que ha incluido, incluya el código calificado. De lo contrario, creo que hay mucho espacio en blanco que se puede aprovechar.
Alex A.

1
@AlexA. el espacio inicial y las nuevas líneas para sangría no se cuentan (y se pueden eliminar de forma segura) Érase una vez que esto estaba permitido y nadie se quejó.
edc65

1
@ edc65 El meta consenso ahora es que se debe proporcionar el código tal como se calificó.
Alex A.

2
Pues bien, la mayoría prefiere la versión ilegible. Todavía dejo la versión legible aquí, en caso de que alguien quiera ver lo que realmente está sucediendo =)
error

2
Es una práctica común proporcionar tanto la presentación de golf (la que está anotada) como una versión sin golf, solo requerimos que se incluya la anotada. ;)
Alex A.

7

Python 2, 151 140 138 bytes

Implementación lenta y recursiva de la distancia de Levenshtein basada en Wikipedia (Gracias a @Kenney por el afeitado de 11 caracteres y @ Sherlock9 por otros 2).

def l(s,t):
 def f(m,n):
  if m*n<1:return m or n
  return 1+min([f(m-1,n),f(m,n-1),f(m-1,n-1)-(s[m-1]==t[n-1])])
 return f(len(s),len(t))

Dando las respuestas correctas para los casos de prueba presentados:

assert l("tar", "tarp") == 1
assert l("turing", "tarpit") == 4
assert l("antidisestablishmentarianism", "bulb") == 27        
assert l("atoll", "bowl") == 3

1
Es posible guardar 3-4 bytes o menos por hacer algo así if !n*m:return n if n else m, y otro 2 por return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ]).
Kenney

Ahorraría 2 bytes usando en f(m-1,n-1)-(s[m-1]==t[n-1])lugar de f(m-1,n-1)+(s[m-1]!=t[n-1])-1.
Sherlock9


5

JavaScript (ES6) 106 113 122

Edite 16 bytes guardados siguiendo las sugerencias de @Neil

Como una función anónima.

(s,t)=>[...s].map((u,i)=>w=w.map((v,j)=>p=j--?Math.min(p,v,w[j]-(u==t[j]))+1:i+1),w=[...[,...t].keys()])|p

Esta es una implementación de golf del algoritmo Wagner-Fischer exactamente como se describe en el artículo de Wikipedia vinculado, en la sección Iterativo con dos filas de matriz (incluso si de hecho, solo se usa 1 fila - matriz w )

Menos golf

(s,t)=>
{
  w = [...[0,...t].keys()];
  for(i = 0; i < s.length; i++)
    w = w.map((v,j)=>
              p = j
              ? Math.min(p+1, v+1, w[j-1] + (s[i]!=t[j-1]))
              : i+1
             );
  return p
}

Fragmento de prueba

L=(s,t)=>[...s].map((u,i)=>w=w.map((v,j)=>p=j--?Math.min(p,v,w[j]-(u==t[j]))+1:i+1),w=[...[,...t].keys()])|p

console.log=x=>O.textContent+=x+'\n';

[["atoll", "bowl"],["tar", "tarp"]
,["turing", "tarpit"],["antidisestablishmentarianism", "bulb"]]
.forEach(t=>console.log(t+' => '+L(...t)))
<pre id=O></pre>


1
¿Puedes usar [...[0,...t].keys()]en su lugar? Ahorra 2 bytes si puedes.
Neil

1
@Neil que se ve feo pero es más corto. Thx
edc65

1
En realidad, puedes guardar otro byte, [...[,...t].keys()]también funciona, creo.
Neil

Me las arreglé para afeitarme otro byte usando [...s].map():(s,t)=>(w=[...[,...t].keys()],[...s].map((u,i)=>w=w.map((v,j)=>p=j--?Math.min(p,v,w[j]-(s[i-1]==t[j]))+1:i)),p)
Neil

@Neil genial, gracias de nuevo!
edc65

4

Python 2, 118 bytes

Un campo de golf de esta solución , pero no parece que Willem haya estado funcionando durante un año, así que tendré que publicarlo yo mismo:

def l(s,t):f=lambda m,n:m or n if m*n<1else-~min(f(m-1,n),f(m,n-1),f(m-1,n-1)-(s[m-1]==t[n-1]));print f(len(s),len(t))

Probar repl.it

Toma dos cadenas y genera la distancia a STDOUT( permitido por meta ). Por favor comente sugerencias, estoy seguro de que esto se puede jugar más.


¿Es necesario envolver todo en una función? ¿Podrías usar dos input()so an input().split()?
Sherlock9

@ Sherlock9 Intenté eso, pero cuesta 1 byte extra por lo que puedo decir
FlipTack

Bien, olvidé que necesitas definir sy ten algún lugar del código. No importa. Buen trabajo: D
Sherlock9

No estoy seguro de por qué Willem usó m or n. Puedes reemplazarlo con m+n.
Arnauld

3

AutoIt , 333 bytes

Func l($0,$1,$_=StringLen,$z=StringMid)
Dim $2=$_($0),$3=$_($1),$4[$2+1][$3+1]
For $5=0 To $2
$4[$5][0]=$5
Next
For $6=0 To $3
$4[0][$6]=$6
Next
For $5=1 To $2
For $6=1 To $3
$9=$z($0,$5,1)<>$z($1,$6,1)
$7=1+$4[$5][$6-1]
$8=$9+$4[$5-1][$6-1]
$m=1+$4[$5-1][$6]
$m=$m>$7?$7:$m
$4[$5][$6]=$m>$8?$8:$m
Next
Next
Return $4[$2][$3]
EndFunc

Código de prueba de muestra:

ConsoleWrite(l("atoll", "bowl") & @LF)
ConsoleWrite(l("tar", "tarp") & @LF)
ConsoleWrite(l("turing", "tarpit") & @LF)
ConsoleWrite(l("antidisestablishmentarianism", "bulb") & @LF)

rendimientos

3
1
4
27

3

k4, 66 bytes

{$[~#x;#y;~#y;#x;&/.z.s'[-1 0 -1_\:x;0 -1 -1_\:y]+1 1,~(*|x)=*|y]}

Una implicación aburrida y básicamente no golfista del algo. Ex.:

  f:{$[~#x;#y;~#y;#x;&/.z.s'[-1 0 -1_\:x;0 -1 -1_\:y]+1 1,~(*|x)=*|y]}
  f["kitten";"sitting"]
3
  f["atoll";"bowl"]
3
  f["tar";"tarp"]
1
  f["turing";"tarpit"]
4
  f["antidisestablishmentarianism";"bulb"]
27

3

En serio, 86 82 78 bytes

,#,#`k;;;░="+l"£@"│d);)[]oq╜Riu)@d);)@[]oq╜Riu(@)@)@[]oq╜Ri3}@)=Y+km"£@IRi`;╗ƒ

Hex Dump:

2c232c23606b3b3b3bb03d222b6c229c4022b364293b295b5d6f71bd526975294064293b29405b
5d6f71bd5269752840294029405b5d6f71bd5269337d40293d592b6b6d229c40495269603bbb9f

Pruébalo en línea

(Tenga en cuenta que el enlace es a una versión diferente porque algo sobre el intérprete en línea rompe con la nueva versión más corta, aunque funciona bien con el intérprete descargable).

Se trata de la implementación más sencilla. En serio, permite la definición recursiva. Es muy lento porque no memoriza nada. Tal vez el método tabular podría ser más corto (tal vez usando los registros como filas), pero estoy bastante contento con esto, a pesar de las deficiencias de kludging-my-way-around-the-language que contiene. Que uno puede usar

[]oq`<code>`Ri

como una llamada a la función de dos argumentos fue un buen hallazgo.

Explicación:

,#,#                             Read in two arguments, break them into lists of chars
    `                       `;╗ƒ put the quoted function in reg0 and immediately call it
     k;;;                        put the two lists in a list and make 3 copies
         ░                       replace the latter two with one with empty lists removed
          =                      replace two more with 1 if no empty lists removed, else 0
           "..."£@"..."£@        push the two functions described below, moving 
                                 the boolean above them both
                         I       select the correct function based on the condition
                          Ri     call the function, returning the correct distance
                                 for these substrings

   There are two functions that can be called from the main function above. Each expects 
   two strings, i and j, to be on the stack. This situation is ensured by putting 
   those strings in a list and using R to call the functions with that list as the stack.
   The first is very simple:

+l                             Concatenate the strings and take their length.
                               This is equivalent to the length of the longer
                               string, since one of the strings will be empty.

   The second function is very long and complicated. It will do the "insertion, deletion, 
   substitution" part of the recursive definition. Here's what happens in 4 parts:

│d);)                          After this, the stack is top[i-,j,i,j,ci,i-], where i- is 
                               list i with its last character, ci, chopped off.
     []oq                      this puts i- and j into a list so that they can be passed
                               as arguments recursively into the main function
         ╜Riu                  this calls the main function (from reg0) with the args
                               which will return a number to which we add 1 to get #d,
                               the min distance if we delete a character
)@d);)@                        After this, the stack is top[i,j-,ci,i-,#d,cj,j-], where 
                               j- and cj are the same idea as i- and ci
       []oq╜Riu                listify arguments, recurse and increment to get #i
                               (distance if we insert)
(@)@)@                         After this, the stack is top[i-,j-,#d,cj,#i,ci]
      []oq╜Ri                  listify arguments, recurse to get min distance between 
                               them but we still need to add 1 when we'd need to 
                               substitute because the chars we chopped off are different
(((@)                          After this, the stack is top[cj,ci,#s,#d,#i]
     =Y                        1 if they are not equal, 0 if they are
       +                       add it to the distance we find to get the distance
                               if we substitute here
        k                      put them all in a list
         m                     push the minimum distance over the three options

Me gusta cómo el código intenta escapar del elemento previo :)
mınxomaτ

3

Python 3, 267 216 184 162 bytes

Esta función calcula la distancia de Levenshtein usando una matriz 2 x len(word_2)+1de tamaño.

Editar: Esto no se acerca a la respuesta de Willem's Python 2, pero aquí hay una respuesta más precisa con muchos pequeños detalles aquí y allá.

def e(p,q):
 m=len(q);r=range;*a,=r(m+1);b=[1]*-~m
 for i in r(len(p)):
  for j in r(m):b[j+1]=1+min(a[j+1],b[j],a[j]-(p[i]==q[j]))
  a,b=b,[i+2]*-~m
 return a[m]

Sin golf:

def edit_distance(word_1,word_2):
    len_1 = len(word_1)
    len_2 = len(word_2)
    dist = [[x for x in range(len_2+1)], [1 for y in range(len_2+1)]]
    for i in range(len_1):
        for j in range(len_2):
            if word_1[i] == word_2[j]:
                dist[1][j+1] = dist[0][j]
            else:
                deletion = dist[0][j+1]+1
                insertion = dist[1][j]+1
                substitution = dist[0][j]+1
                dist[1][j+1] = min(deletion, insertion, substitution)
        dist[0], dist[1] = dist[1], [i+2 for m in range(len_2+1)]
    return dist[0][len_2]

3

Retina , 78 72 bytes

&`(.)*$(?<!(?=((?<-4>\4)|(?<-1>.(?<-4>)?))*,(?(4),))^.*,((.)|(?<-1>.))*)

Pruébalo en línea!

En cierto modo, esta es una solución pura de expresiones regulares donde el resultado es el número de posiciones desde las cuales coincide la expresión regular. Porque, porque no...

Advertencia justa, esto es super ineficiente. La forma en que esto funciona es que descarga la optimización real al backtracker del motor regex, que simplemente fuerza bruta todas las alineaciones posibles, comenzando con la menor cantidad de alteraciones posible y permitiendo una más hasta que sea posible combinar las cadenas con adiciones, eliminaciones y sustituciones .

Para una solución un poco más sensata, esta hace la correspondencia solo una vez y no tiene ningún aspecto negativo. Aquí, el resultado es el número de capturas en grupo 2, a las que puede acceder match.Groups[2].Captures.Counten C #, por ejemplo. Sin embargo, todavía es terriblemente ineficiente.

Explicación

Estoy explicando la segunda versión anterior, porque conceptualmente es un poco más fácil (ya que es solo una coincidencia de expresiones regulares). Aquí hay una versión sin golf que he nombrado los grupos (o los hice no capturadores) y agregué comentarios. Recuerde que los componentes en una vista posterior deben leerse de atrás hacia adelante, pero las alternativas y las miradas dentro de ellas deben leerse de adelante hacia atrás. Sí.

.+                      # Ensures backtracking from smallest to largest for next repetition
(?<ops>(?<distance>.))* # This puts the current attempted distances onto two different stacks,
                        # one to work with, and one for the result.
$                       # Make sure the lookbehind starts from the end.
(?<=                    # The basic idea is now to match up the strings character by character,
                        # allowing insertions/deletions/substitutions at the cost of one capture
                        # on <ops>. Remember to read from the bottom up.
  (?=                   # Start matching forwards again. We need to go through the other string
                        # front-to-back due to the nature of the stack (the last character we
                        # remembered from the second string must be the first character we check
                        # against in the first string).
    (?:
      (?<-str>\k<str>)  # Either match the current character against the corresponding one from
                        # the other string.
    |
      (?<-ops>          # Or consume one operation to...
        .               # consume a character without matching it to the other string (a deletion)
        (?<-str>)?      # optionally dropping a character from the other string as well 
                        # (a substitution).
      )
    )*                  # Rinse and repeat.
    ,(?(str),)          # Ensure we reached the end of the first string while consuming all of the 
                        # second string. This is only possible if the two strings can be matched up 
                        # in no more than <distance> operations.
  )
  ^.*,                  # Match the rest of string to get back to the front.
  (?:                   # This remembers the second string from back-to-front.
    (?<str>.)           # Either capture the current character.
  |
    (?<-ops>.)          # Or skip it, consuming an operation. This is an insertion.
  )*
)

La única diferencia con la versión de 72 bytes es que podemos eliminar el líder .+(y el segundo grupo al principio) al encontrar posiciones al final donde no tenemos suficiente <ops>y contar todas esas posiciones.


3

Haskell , 67 64 bytes

e@(a:r)#f@(b:s)=sum[1|a/=b]+minimum[r#f,e#s,r#s]
x#y=length$x++y

Pruébalo en línea! Ejemplo de uso: "turing" # "tarpit"rendimientos 4.


Explicación (para la versión anterior de 67 bytes)

e@(a:r)#f@(b:s)|a==b=r#s|1<3=1+minimum[r#f,e#s,r#s]
x#y=length$x++y

Esta es una solución recursiva. Dadas dos cadenas ey f, primero comparamos sus primeros personajes ay b. Si son iguales, entonces la distancia de Levenshtein de ey fes la misma que la distancia de Levenshtein de ry s, el resto de ey fdespués de eliminar los primeros caracteres. De lo contrario, ao bnecesita ser eliminado, o uno es sustituido por el otro. [r#f,e#s,r#s]calcula recursivamente el Levenshtein para esos tres casos, minimumelige el más pequeño y 1se agrega para tener en cuenta la operación necesaria de eliminación o reemplazo.

Si una de las cadenas está vacía, nosotros y arriba en la segunda línea. En este caso, la distancia es solo la longitud de la cadena no vacía, o equivalentemente la longitud de ambas cadenas concatenadas juntas.


1
Wow, esta es una solución realmente buena, realmente elegante y corta.
ggPeti

3

Python 3 , 105 94 93 bytes

-11 bytes por xnor

l=lambda a,b:b>""<a and min(l(a[1:],b[1:])+(a[0]!=b[0]),l(a[1:],b)+1,l(a,b[1:])+1)or len(a+b)

Versión de golf de la implementación más corta en Wikilibros .

Pruébalo en línea!


Buena solución Las l=necesidades que deben incluirse y se contaron porque la función es recursivo. Puede combinar los casos base en if b>""<a else len(a+b).
xnor

Buen juego con los operadores, gracias!
movatica

2

Haskell, 136 bytes

Llamada e. Poco lento, antidisestablishmentarianismetc.

l=length
e a b=v a(l a)b(l b)
v a i b j|i*j==0=i+j|0<1=minimum[1+v a(i-1)b j,1+v a i b(j-1),fromEnum(a!!(i-1)/=b!!(j-1))+v a(i-1)b(j-1)]

2

Jolf, 4 bytes

Pruébalo aquí!

~LiI
~L   calculate the Levenshtein distance of
  i   input string
   I  and another input string

Agregué esa construcción ayer, pero vi este desafío hoy, es decir, justo ahora. Aún así, esta respuesta no es competitiva.

En una versión más nueva:

~Li

toma una segunda entrada implícita.


" Su código debe ser un programa o una función. No necesita ser una función con nombre, pero no puede ser una función integrada que calcule directamente la distancia de Levenshtein . Se permiten otras
funciones integradas

Ah, no vi que mencionaste que no compite ... Mejor ponlo en el título, o agrega un programa / función válido sin incluirlo.
Kevin Cruijssen

2

GNU Prolog, 133 bytes

m([H|A],B):-B=A;B=[H|A].
d([H|A]-[H|B],D):-d(A-B,D).
d(A-B,D):-A=B,D=0;D#=E+1,m(A,X),m(B,Y),d(X-Y,E).
l(W,D):-d(W,D),(E#<D,l(W,E);!).

Toma una tupla como argumento. Ejemplo de uso:

| ?- l("turing"-"tarpit",D).

D = 4

yes

mespecifica que Bes Adirectamente o con su primer carácter eliminado. dse usa mcomo subrutina para calcular una distancia de edición entre los elementos de tupla (es decir, la distancia de una serie de ediciones que convierte una en la otra). Entonces les un truco estándar para encontrar el mínimo de d(toma una distancia arbitraria, luego toma una distancia arbitraria más pequeña, repite hasta que no puedas hacerlo más pequeño).


1

Perl, 168 166 163 bytes

sub l{my($S,$T,$m)=(@_,100);$S*$T?do{$m=++$_<$m?$_:$m for l($S-1,$T),l($S,--$T),l(--$S,$T)-($a[$S]eq$b[$T]);$m}:$S||$T}print l~~(@a=shift=~/./g),~~(@b=shift=~/./g)

Implementación recursiva. Guardar en file.ply correr como perl file.pl atoll bowl.

sub l {
    my($S,$T,$m)=(@_,100);

    $S*$T
    ? do {
        $m = ++$_ < $m ? $_ : $m
        for
            l($S-1,   $T),
            l($S  , --$T),
            l(--$S,   $T) - ($a[$S] eq $b[$T])
        ;    
        $m
    }
    : $S||$T
}
print l~~(@a=shift=~/./g),~~(@b=shift=~/./g)


Las otras dos implementaciones son más largas (matriz completa: 237 bytes, dos iterativas de una fila: 187).

  • Actualización 166 : omitir ()al llamar l.
  • Actualización 163 : eliminar returnmediante el abuso doen trinario.


0

C, 192 bytes

#define m(x,y) (x>y?x:y)
#define r(x,y) l(s,ls-x,t,lt-y)
int l(char*s,int ls,char*t,int lt){if(!ls)return lt;if(!lt)return ls;a=r(1,1);if(s[ls]==t[ls])return a;return m(m(r(0,1),r(1,0)),a)+1;}
---------

Detallado

#include <stdio.h>

#define m(x,y) (x>y?x:y)
#define f(x) char x[128];fgets(x,100,stdin)
#define r(x,y) l(s,ls-x,t,lt-y)

int l(char*s,int ls,char*t,int lt)
{
    if(!ls) return lt;
    if(!lt) return ls;

    int a = r(1,1);
    if(s[ls]==t[ls]) return a;

    return m(m(r(0,1),r(1,0)),a)+1;
}

int main(void)
{
    f(a);
    f(b);
    printf("%d", l(a,strlen(a),b,strlen(b)));
    return 0;
}

0

C #, 215 210 198

public int L(string s,string t){int c,f,a,i,j;var v=new int[100];for(c=i=0;i<s.Length;i++)for(f=c=i,j=0;j<t.Length;j++){a=c;c=f;f=i==0?j+1:v[j];if(f<a)a=f;v[j]=c=s[i]==t[j]?c:1+(c<a?c:a);}return c;}

más legible:

public int L(string s,string t){
    int c,f,a,i,j;
    var v=new int[100];
    for(c=i=0;i<s.Length;i++)
        for(f=c=i,j=0;j<t.Length;j++){
            a=c;
            c=f;
            f=(i==0)?j+1:v[j];
            if (f<a) a=f;
            v[j]=c=(s[i]==t[j])?c:1+((c<a)?c:a);
        }
    return c;
}

0

PowerShell v3 +, 247 bytes

$c,$d=$args;$e,$f=$c,$d|% le*;$m=[object[,]]::new($f+1,$e+1);0..$e|%{$m[0,$_]=$_};0..$f|%{$m[$_,0]=$_};1..$e|%{$i=$_;1..$f|%{$m[$_,$i]=(($m[($_-1),$i]+1),($m[$_,($i-1)]+1),($m[($_-1),($i-1)]+((1,0)[($c[($i-1)]-eq$d[($_-1)])]))|sort)[0]}};$m[$f,$e]

Terminé haciendo esto para resolver otros desafíos relacionados con los LD.

Explicación del código con comentarios.

# Get both of the string passed as arguments. $c being the compare string
# and $d being the difference string. 
$c,$d=$args

# Save the lengths of these strings. $e is the length of $c and $f is the length of $d
$e,$f=$c,$d|% le*

# Create the multidimentional array $m for recording LD calculations
$m=[object[,]]::new($f+1,$e+1)

# Populate the first column 
0..$e|%{$m[0,$_]=$_}

# Populate the first row
0..$f|%{$m[$_,0]=$_}

# Calculate the Levenshtein distance by working through each position in the matrix. 
# Working the columns
1..$e|%{
    # Save the column index for use in the next pipeline
    $i=$_

    # Working the rows.
    1..$f|%{
        # Calculate the smallest value between the following values in the matrix relative to this one
        # cell above, cell to the left, cell in the upper left. 
        # Upper left also contain the cost calculation for this pass.    
        $m[$_,$i]=(($m[($_-1),$i]+1),($m[$_,($i-1)]+1),($m[($_-1),($i-1)]+((1,0)[($c[($i-1)]-eq$d[($_-1)])]))|sort)[0]
    }
}
# Return the last element of the matrix to get LD 
$m[$f,$e]

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.