Odległość Levenshteina


39

Chociaż istnieje wiele pytań edycji odległości, takich jak to , nie ma prostego pytania, aby napisać program, który oblicza odległość Levenshteina.

Jakaś ekspozycja

Odległość edycji Levenshteina między dwoma łańcuchami to minimalna możliwa liczba wstawek, usunięć lub podstawień w celu konwersji jednego słowa na inne. W takim przypadku każde wstawienie, usunięcie i zastąpienie kosztuje 1.

Na przykład odległość między rolli rollingwynosi 3, ponieważ usunięcie kosztuje 1, a my musimy usunąć 3 znaki. Odległość między tolli tallwynosi 1, ponieważ zamiana kosztuje 1.

Zasady

  • Dane wejściowe będą miały dwa ciągi. Możesz założyć, że łańcuchy są małe, zawierają tylko litery, nie są puste i mają maksymalnie 100 znaków.
  • Wyjście będzie minimalną odległością edycyjną Levenshteina dwóch łańcuchów, jak zdefiniowano powyżej.
  • Twój kod musi być programem lub funkcją. Nie musi to być funkcja nazwana, ale nie może to być funkcja wbudowana, która bezpośrednio oblicza odległość Levenshteina. Inne wbudowane są dozwolone.
  • To jest golf golfowy, więc wygrywa najkrótsza odpowiedź.

Kilka przykładów

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

Jak zawsze, jeśli problem jest niejasny, daj mi znać. Powodzenia i dobrej gry w golfa!

Katalog

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>

Odpowiedzi:


8

Pyth, 34 bajty

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

Demonstracja

Nie jest to szczególnie dobrze gra w golfa i jest bardzo wolne. Nie jest w stanie obsłużyć niczego po 2 zmianach w rozsądnym czasie.


3
Ale to działa i to się liczy. : P
Conor O'Brien

10

Matlab, 177 163 bajtów

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)

Jest to prosta implementacja tej formuły:

wprowadź opis zdjęcia tutaj

Nie golfowany:

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)

Jeśli punktowany kod nie jest tym, co podałeś, dołącz kod punktowany. W przeciwnym razie myślę, że istnieje wiele białych znaków, które można zagrać w golfa.
Alex A.,

1
@AlexA. wiodące miejsce i znaki nowej linii dla celów wcięcia nie są liczone (i można je bezpiecznie usunąć). Pewnego razu było to dozwolone i nikt nie narzekał.
edc65,

1
@ edc65 Meta konsensus polega obecnie na tym, że należy podać kod w punktacji.
Alex A.,

2
Cóż, większość woli wersję nieczytelną. Nadal udostępniam tutaj czytelną wersję, na wypadek gdyby ktoś chciał zobaczyć, co się właściwie dzieje =)
flawr

2
Powszechną praktyką jest dostarczanie zarówno wyniku golfowego (tego, który został oceniony), jak i wersji bez golfa, po prostu wymagamy, aby uwzględniono jeden wynik. ;)
Alex A.

7

Python 2, 151 140 138 bajtów

Powolna rekurencyjna implementacja odległości Levenshtein na podstawie Wikipedii (dzięki @Kenney za golenie 11 znaków i @ Sherlock9 za kolejne 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))

Podanie poprawnych odpowiedzi dla przedstawionych przypadków testowych:

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

1
Możesz zaoszczędzić około 3-4 bajtów, robiąc coś podobnego if !n*m:return n if n else m, a kolejne dwa przez return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ]).
Kenney,

Zaoszczędziłbyś 2 bajty, używając f(m-1,n-1)-(s[m-1]==t[n-1])zamiast f(m-1,n-1)+(s[m-1]!=t[n-1])-1.
Sherlock9,

Grał w golfa poza 20 znakami
FlipTack

5

JavaScript (ES6) 106 113 122

Edytuj 16 bajtów zapisanych zgodnie z sugestiami @Neil

Jako funkcja anonimowa.

(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

Jest to golfowa implementacja algorytmu Wagnera-Fischera dokładnie taka, jak opisano w powiązanym artykule na Wikipedii, w sekcji Iteracyjna z dwoma wierszami macierzy (nawet jeśli w rzeczywistości używany jest tylko 1 wiersz - tablica w )

Mniej golfa

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

Testowy fragment kodu

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
Czy możesz użyć [...[0,...t].keys()]zamiast tego? Zapisuje 2 bajty, jeśli możesz.
Neil,

1
@ Neil, który wygląda brzydko, ale jest krótszy. Thx
edc65

1
Właściwie możesz zapisać kolejny bajt, [...[,...t].keys()]myślę, że też działa.
Neil,

Udało mi się ogolić kolejny bajt, używając [...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 świetnie, jeszcze raz dziękuję!
edc65,

4

Python 2, 118 bajtów

Golf tego rozwiązania , ale nie wygląda na to, że Willem był aktywny od roku, więc muszę go opublikować:

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

Spróbuj na repl.it

Pobiera dwa ciągi i podaje odległość do STDOUT( dozwoloną przez meta ). Proszę skomentować sugestie, jestem pewien, że można to jeszcze pograć w golfa.


Czy konieczne jest zawijanie wszystkiego w funkcję? Czy możesz użyć dwóch input()s lub an input().split()?
Sherlock9,

@ Sherlock9 Próbowałem tego, ale kosztuje 1 bajt więcej, o ile mogę powiedzieć
FlipTack

Racja, zapomniałem, że musisz zdefiniować si tgdzieś w kodzie. Nieważne. Dobra robota: D
Sherlock9,

Nie jestem pewien, dlaczego Willem go użył m or n. Możesz go zastąpić m+n.
Arnauld

3

AutoIt , 333 bajty

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

Przykładowy kod testowy:

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

daje

3
1
4
27

3

k4, 66 bajtów

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

Nudny i w zasadzie nie golfisty implant algo. Dawny.:

  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

Poważnie, 86 82 78 bajtów

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

Hex Dump:

2c232c23606b3b3b3bb03d222b6c229c4022b364293b295b5d6f71bd526975294064293b29405b
5d6f71bd5269752840294029405b5d6f71bd5269337d40293d592b6b6d229c40495269603bbb9f

Wypróbuj online

(Pamiętaj, że link prowadzi do innej wersji, ponieważ coś w tłumaczeniu online jest zepsute z nową, krótszą wersją, mimo że działa dobrze z tłumaczem do pobrania).

Chodzi o najprostszą implementację. Poważnie pozwala na definicję rekurencyjną. Jest bardzo powolny, ponieważ w ogóle nie zapamiętuje. Być może metoda tabelaryczna może być krótsza (być może przy użyciu rejestrów jako wierszy), ale jestem z tego całkiem zadowolony, pomimo tego, jak wiele zawiera niedociągnięć w mowie. Tego można użyć

[]oq`<code>`Ri

jako prawidłowe wywołanie funkcji dwuparlamentarnej było całkiem miłym znaleziskiem.

Wyjaśnienie:

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

Podoba mi się, jak kod próbuje uciec przed pre-elementem :)
mınxomaτ

3

Python 3, 267 216 184 162 bajtów

Ta funkcja oblicza odległość Levenshteina przy użyciu odpowiedniej wielkości tablicy 2 x len(word_2)+1.

Edycja: Nie zbliża się to do odpowiedzi Willem's Python 2, ale tutaj jest bardziej golfowa odpowiedź z wieloma drobnymi udoskonaleniami tu i tam.

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]

Nie golfowany:

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 bajtów

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

Wypróbuj online!

W pewnym sensie jest to czyste rozwiązanie wyrażenia regularnego, w którym wynikiem jest liczba pozycji, z którymi pasuje wyrażenie regularne. Bo czemu nie...

Uczciwe ostrzeżenie, jest to bardzo nieefektywne. Działa to w ten sposób, że odciąża rzeczywistą optymalizację do backtrackera silnika regex, który po prostu brutalnie wymusza wszystkie możliwe wyrównania, zaczynając od jak najmniejszej liczby zmian i pozwalając na jeszcze jedną, aż możliwe będzie dopasowanie ciągów z dodatkami, usunięciami i podstawieniami .

W przypadku nieco bardziej sensownego rozwiązania, ten dopasowuje tylko raz i nie ma żadnych negatywnych spojrzeń. Tutaj wynikiem jest liczba przechwyceń w grupie 2, do których można uzyskać dostęp match.Groups[2].Captures.Countna przykład w języku C #. Jest to jednak nadal okropnie nieefektywne.

Wyjaśnienie

Wyjaśniam drugą wersję powyżej, ponieważ jest ona koncepcyjnie nieco łatwiejsza (ponieważ jest to tylko jedno dopasowanie wyrażenia regularnego). Oto wersja bez golfisty Nazwałam grupy (lub uczyniłam je nie przechwytującymi) i dodałem komentarze. Pamiętaj, że komponenty w wyglądzie powinny być odczytywane od tyłu do przodu, ale alternatywy i spojrzenia wewnątrz nich powinny być odczytywane od przodu do tyłu. Tak.

.+                      # 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.
  )*
)

Jedyną różnicą w stosunku do 72-bajtowej wersji jest to, że możemy porzucić pozycję wiodącą .+(i drugą grupę na początku), znajdując pozycje na końcu, gdzie nie mamy wystarczającej <ops>liczby, i zliczamy wszystkie te pozycje.


3

Haskell , 67 64 bajtów

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

Wypróbuj online! Przykładowe użycie: "turing" # "tarpit"daje 4.


Objaśnienie (dla poprzedniej 67-bajtowej wersji)

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

To jest rozwiązanie rekurencyjne. Biorąc pod uwagę dwa ciągi ei f, najpierw porównujemy ich pierwsze znaki ai b. Jeśli są one równe, to odległość Levenshteina ei fjest taka sama jak odległość Levenshteina ri s, pozostała część ei fpo usunięciu pierwszych znaków. W przeciwnym razie albo aalbo bmusi zostać usunięty, albo jeden jest zastąpiony drugim. [r#f,e#s,r#s]rekurencyjnie oblicza Levenshtein dla tych trzech przypadków, minimumwybiera najmniejszą i 1dodaje, aby uwzględnić niezbędną operację usunięcia lub zastąpienia.

Jeśli jeden z łańcuchów jest pusty, my i na drugim wierszu. W tym przypadku odległość jest tylko długością niepustego łańcucha lub równoważnie długością obu łańcuchów połączonych razem.


1
Wow, to naprawdę dobre rozwiązanie, naprawdę eleganckie i krótkie.
ggPeti

3

Python 3 , 105 94 93 bajty

-11 bajtów przez 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)

Wersja najkrótsza implementacja na Wikibooks .

Wypróbuj online!


Niezłe rozwiązanie. Należy l=uwzględnić i policzyć, ponieważ funkcja jest rekurencyjna. Możesz połączyć przypadki baz w if b>""<a else len(a+b).
xnor

Miłej zabawy z operatorami, dzięki!
movatica

2

Haskell, 136 bajtów

Zadzwoń e. Trochę wolno na antidisestablishmentarianismitp.

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 bajty

Wypróbuj tutaj!

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

Dodałem to wbudowane wczoraj, ale widziałem to wyzwanie dzisiaj, czyli właśnie teraz. Mimo to ta odpowiedź nie jest konkurencyjna.

W nowszej wersji:

~Li

przyjmuje niejawne drugie wejście.


Twój kod musi być programem lub funkcją. Nie musi to być funkcja nazwana, ale nie może to być funkcja wbudowana, która bezpośrednio oblicza odległość Levenshteina . Inne wbudowane funkcje są dozwolone.
Kevin Cruijssen

Ach, nie widziałem, żebyś wspomniał, że to nie konkuruje. Lepiej umieść to w tytule lub dodaj prawidłowy program / funkcję bez wbudowanego.
Kevin Cruijssen

2

GNU Prolog, 133 bajty

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

Przyjmuje krotkę jako argument. Przykład użycia:

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

D = 4

yes

mokreśla, że Bjest Aalbo bezpośrednio, albo z usuniętym pierwszym znakiem. dzastosowania mjako podprogram do wyliczenia się odległość między elementami edycji krotka (czyli odległość serii edycji, który przekształca się w drugą). Wtedy lto standardowy trik do znalezienia minimum d(wziąć dowolną odległość, a następnie podjąć dowolny mniejszy dystans, powtórki, aż nie można się mniejszy).


1

Perl, 168 166 163 bajtów

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)

Implementacja rekurencyjna. Zapisz w file.pli uruchom jako 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)


Pozostałe dwie implementacje są dłuższe (pełna matryca: 237 bajtów, dwie iteracyjne jednorzędowe: 187).

  • aktualizacja 166 : pomiń ()w dzwonieniu l.
  • aktualizacja 163 : wyeliminuj returnprzez nadużywanie dow trynku


0

C, 192 bajty

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

Szczegółowy

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

bardziej czytelny:

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 bajtów

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

Skończyło się to na rozwiązaniu innych problemów związanych z LD.

Wyjaśnienie kodu z komentarzami.

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.