Levenshtein Distance


39

Bien qu'il existe de nombreuses questions de distance d'édition, comme celle-ci , il n'y a pas de question simple pour écrire un programme calculant la distance de Levenshtein.

Une Exposition

La distance de levée Levenshtein entre deux chaînes est le nombre minimal possible d'insertions, de suppressions ou de substitutions pour convertir un mot en un autre. Dans ce cas, chaque insertion, suppression et substitution a un coût de 1.

Par exemple, la distance entre rollet rollingest égale à 3, car les suppressions coûtent 1 et nous devons supprimer 3 caractères. La distance entre tollet tallest égale à 1, car les substitutions coûtent 1.

Règles

  • L'entrée sera deux chaînes. Vous pouvez supposer que les chaînes sont en minuscules, ne contiennent que des lettres, ne sont pas vides et ont une longueur maximale de 100 caractères.
  • La sortie correspondra à la distance minimale d’édition de Levenshtein des deux chaînes, comme défini ci-dessus.
  • Votre code doit être un programme ou une fonction. Il ne doit pas nécessairement s'agir d'une fonction nommée, mais il ne peut s'agir d'une fonction intégrée qui calcule directement la distance de Levenshtein. Les autres fonctions intégrées sont autorisées.
  • C'est le code de golf, donc la réponse la plus courte gagne.

Quelques exemples

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

Comme toujours, si le problème n'est pas clair, merci de me le faire savoir. Bonne chance et bon golf!

Catalogue

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>

Réponses:


8

Pyth, 34 octets

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

Manifestation

Ce n'est pas particulièrement bien joué au golf et très lent. Il ne peut gérer aucune modification passée au-delà de 2 modifications dans un délai raisonnable.


3
Mais cela fonctionne et c'est ce qui compte. : P
Conor O'Brien

10

Matlab, 177 163 octets

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)

Ceci est une implémentation simple de cette formule:

enter image description here

Ungolfed:

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 le code marqué n'est pas ce que vous avez inclus, veuillez l'inclure. Sinon, je pense qu'il y a beaucoup d'espaces qui peuvent être joués.
Alex A.

1
@Alexa. les espaces de début et les nouvelles lignes pour l'indentation ne sont pas comptés (et peuvent être supprimés en toute sécurité). Il était une fois que cela était autorisé et que personne ne se plaint.
edc65

1
@ edc65 Le méta-consensus est maintenant que le code tel que noté devrait être fourni.
Alex A.

2
Eh bien, la majorité préfère la version illisible. Je laisse toujours la version lisible ici, au cas où quelqu'un voudrait voir ce qui se passe réellement =)
flawr

2
Il est de pratique courante de fournir à la fois la soumission golfée (celle qui a été notée) et une version non-golfée. Nous exigeons simplement que celle-ci soit incluse. ;)
Alex A.

7

Python 2, 151 140 138 octets

Mise en œuvre lente et récursive de la distance de Levenshtein basée sur Wikipedia (Merci à @Kenney pour le rasage de 11 caractères et à @ Sherlock9 pour un autre 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))

Donner les bonnes réponses pour les cas de test présentés:

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

1
Vous pouvez économiser 3-4 octets ou plus en faisant quelque chose comme if !n*m:return n if n else m, et 2 autres par return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ]).
Kenney

Vous voudriez économiser 2 octets en utilisant à la f(m-1,n-1)-(s[m-1]==t[n-1])place de f(m-1,n-1)+(s[m-1]!=t[n-1])-1.
Sherlock9


5

JavaScript (ES6) 106 113 122

Editez 16 octets sauvegardés suite aux suggestions de @Neil

En tant que fonction anonyme.

(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

Il s’agit d’une implémentation de l’algorithme de Wagner-Fischer dans le golfe, exactement comme décrit dans l’article lié à Wikipédia, dans la section Itérative avec deux lignes de matrice (même si en fait, une seule ligne est utilisée - tableau w ).

Moins 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
}

Extrait de test

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
Pouvez-vous utiliser à la [...[0,...t].keys()]place? Enregistre 2 octets si vous le pouvez.
Neil

1
@ Neil qui a l'air moche mais c'est plus court. Thx
edc65

1
En fait, vous pouvez enregistrer un autre octet, cela [...[,...t].keys()]fonctionne aussi, je pense.
Neil

J'ai réussi à réduire un autre octet en utilisant [...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 super, merci encore!
edc65

4

Python 2, 118 octets

Un exemple de cette solution , mais il ne semble pas que Willem soit sur le marché depuis un an, je vais donc devoir le publier moi-même:

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))

Essayez repl.it

Prend deux chaînes et renvoie la distance à STDOUT( autorisée par meta ). S'il vous plaît commenter les suggestions, je suis sûr que cela peut être joué plus loin.


Est-il nécessaire de tout envelopper dans une fonction? Pourriez-vous utiliser deux input()s ou un input().split()?
Sherlock9

@ Sherlock9 J'ai essayé ça, mais ça coûte un octet en plus , autant que je
sache

D'accord, j'ai oublié que vous devez définir set tquelque part dans le code. Ça ne fait rien. Bon travail: D
Sherlock9

Je ne sais pas pourquoi Willem a utilisé m or n. Vous pouvez le remplacer par m+n.
Arnauld le

3

AutoIt , 333 octets

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

Exemple de code de test:

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

les rendements

3
1
4
27

3

k4, 66 octets

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

Un impl ennuyeux et fondamentalement non-golfé de l'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

Sérieusement, 86 82 78 octets

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

Décharge Hex:

2c232c23606b3b3b3bb03d222b6c229c4022b364293b295b5d6f71bd526975294064293b29405b
5d6f71bd5269752840294029405b5d6f71bd5269337d40293d592b6b6d229c40495269603bbb9f

Essayez-le en ligne

(Notez que le lien est dirigé vers une version différente car quelque chose à propos de l'interprète en ligne rompt avec la nouvelle version plus courte, même si cela fonctionne correctement avec l'interprète téléchargeable.)

Il s’agit de l’implémentation la plus simple qui permet Sérieusement de prendre en compte la définition récursive. C'est très lent parce que ça ne mémoise pas du tout. Peut-être que la méthode tabulaire pourrait être plus courte (peut-être en utilisant les registres comme des lignes), mais je suis assez contente de cela, malgré les faiblesses qu'elle contient. Celui-là peut utiliser

[]oq`<code>`Ri

comme un appel de fonction à deux arguments convenable était une bonne trouvaille.

Explication:

,#,#                             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

J'aime la façon dont le code tente d'échapper au pré-élément :)
mınxomaτ

3

Python 3, 267 216 184 162 octets

Cette fonction calcule la distance de Levenshtein en utilisant un tableau 2 x len(word_2)+1de taille.

Edit: Cela ne se rapproche pas de la réponse de Willem's Python 2, mais voici une réponse plus détaillée avec beaucoup de petites améliorations ici et là.

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]

Ungolfed:

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

Rétine , 78 72 octets

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

Essayez-le en ligne!

En un sens, il s’agit d’une solution purement rationnelle dont le résultat est le nombre de positions à partir desquelles la regex correspond. Parce que pourquoi pas ...

Avertissement juste, c'est super inefficace. La façon dont cela fonctionne consiste à décharger l'optimisation réelle du backtracker du moteur des expressions rationnelles, ce qui force simplement tous les alignements possibles, en commençant par le moins de modifications possibles et en permettant un changement supplémentaire jusqu'à ce qu'il soit possible de faire correspondre les chaînes avec des ajouts, des suppressions et des substitutions. .

Pour une solution légèrement plus judicieuse, celle-ci ne fait l’appariement qu’une seule fois et n’a pas d’apparence négative. Ici, le résultat est le nombre de captures en groupe2 auquel vous pouvez accéder match.Groups[2].Captures.Counten C #, par exemple. C'est quand même horriblement inefficace.

Explication

J'explique la deuxième version ci-dessus, parce que c'est conceptuellement un peu plus facile (étant donné qu'il ne s'agit que d'un seul match regex). Voici une version non golfée que j'ai nommée (ou que je n'ai pas capturée) et que j'ai ajoutée des commentaires. N'oubliez pas que les composants d'un regard arrière doivent être lus de l'arrière vers l'avant, mais que les alternatives et les points d'anticipation à l'intérieur de ceux-ci doivent être lus d'avant en arrière. Ouais.

.+                      # 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 seule différence par rapport à la version à 72 octets est que nous pouvons laisser tomber le groupe de tête .+(et le deuxième groupe au début) en trouvant des positions à la fin où nous n’en avons pas assez <ops>et compter toutes ces positions.


3

Haskell , 67 64 octets

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

Essayez-le en ligne! Exemple d'utilisation: "turing" # "tarpit"rendements 4.


Explication (pour la version précédente de 67 octets)

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

C'est une solution récursive. Étant donné deux chaînes eet f, nous comparons d’abord leurs premiers caractères aet b. S'ils sont égaux, la distance de Levenshtein de eet fest identique à la distance de Levenshtein de ret s, le reste de eet faprès la suppression des premiers caractères. Autrement, l'un aou l' autre bdoit être supprimé, ou l'un est remplacé par l'autre. [r#f,e#s,r#s]calcule récursivement le Levenshtein pour ces trois cas,minimum sélectionne le plus petit et 1est ajouté pour prendre en compte l'opération de suppression ou de remplacement nécessaire.

Si l'une des chaînes est vide, nous remontons à la deuxième ligne. Dans ce cas, la distance est simplement la longueur de la chaîne non vide ou, de manière équivalente, la longueur des deux chaînes concaténées ensemble.


1
Wow, c'est une très bonne solution, vraiment élégante et courte.
ggPeti

3

Python 3 , 105 94 93 octets

-11 octets par 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)

Version golfée de la plus courte implémentation sur Wikibooks .

Essayez-le en ligne!


Belle solution. Le l=doit être inclus et compté car la fonction est récursive. Vous pouvez combiner les cas de base en if b>""<a else len(a+b).
xnor

Beau jeu avec les opérateurs, merci!
movatica

2

Haskell, 136 octets

Appel e. Peu lent sur 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 octets

Essayez-le ici!

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

J'ai ajouté cela hier, mais j'ai vu le défi aujourd'hui, c'est-à-dire tout à l'heure. Pourtant, cette réponse est non compétitive.

Dans une version plus récente:

~Li

prend une deuxième entrée implicite.


" Votre code doit être un programme ou une fonction. Il ne doit pas nécessairement s'agir d'une fonction nommée, mais il ne peut s'agir d'une fonction intégrée qui calcule directement la distance de Levenshtein . Les autres fonctions intégrées sont autorisées. "
Kevin Cruijssen

Ah, je n'ai pas vu que vous aviez mentionné que c'était non compétitif. Mieux vaut le mettre dans le titre ou ajouter un programme / une fonction valide sans fonctions intégrées.
Kevin Cruijssen

2

GNU Prolog, 133 octets

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);!).

Prend un tuple en argument. Exemple d'utilisation:

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

D = 4

yes

mspécifie qu'il Best Adirectement ou avec son premier caractère supprimé. dutilise mcomme sous-routine pour calculer une distance de montage entre les tuple (c’est-à-dire la distance d’une série de modifications qui convertit l’un en l’autre). Il ls’agit alors d’une astuce classique pour trouver le minimum de d(vous prenez une distance arbitraire, puis une distance arbitraire plus petite, répétez jusqu’à ce que vous ne puissiez plus réduire).


1

Perl, 168 166 163 octets

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)

Implantation récursive. Enregistrer dans un file.plet exécuter en tant que 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)


Les deux autres implémentations sont toutes deux plus longues (matrice complète: 237 octets, deux itératives sur une ligne: 187).

  • mise à jour 166 : omettre ()en appelant l.
  • mise à jour 163 : éliminer returnen abusant doen trinary.


0

C, 192 octets

#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;}
---------

Détaillé

#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;}

plus lisible:

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 octets

$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]

J'ai fini par faire ceci pour résoudre un autre défi impliquant les LD.

Explication de code avec commentaires.

# 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]

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.