Distanza di Levenshtein


39

Mentre ci sono molte domande sulla distanza di modifica, come questa , non c'è una semplice domanda per scrivere un programma che calcola la distanza di Levenshtein.

Qualche esposizione

La distanza di modifica di Levenshtein tra due stringhe è il numero minimo possibile di inserzioni, eliminazioni o sostituzioni per convertire una parola in un'altra parola. In questo caso, ogni inserimento, cancellazione e sostituzione ha un costo di 1.

Ad esempio, la distanza tra rolle rollingè 3, poiché le eliminazioni costano 1 e dobbiamo eliminare 3 caratteri. La distanza tra tolle tallè 1, poiché le sostituzioni costano 1.

Regole

  • L'input sarà di due stringhe. Puoi presumere che le stringhe siano minuscole, contengano solo lettere, non siano vuote e abbiano una lunghezza massima di 100 caratteri.
  • L'output sarà la distanza minima di modifica di Levenshtein delle due stringhe, come definito sopra.
  • Il tuo codice deve essere un programma o una funzione. Non deve essere una funzione denominata, ma non può essere una funzione incorporata che calcola direttamente la distanza di Levenshtein. Sono ammessi altri built-in.
  • Questo è il codice golf, quindi vince la risposta più breve.

Qualche esempio

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

Come sempre, se il problema non è chiaro, per favore fatemi sapere. Buona fortuna e buon golf!

Catalogare

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>

Risposte:


8

Pyth, 34 byte

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

Dimostrazione

Questo non è particolarmente ben giocato e molto lento. Non può gestire nulla dopo le 2 modifiche in un periodo di tempo ragionevole.


3
Ma funziona, ed è quello che conta. : P
Conor O'Brien,

10

Matlab, 177 163 byte

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)

Questa è un'implementazione diretta di questa formula:

inserisci qui la descrizione dell'immagine

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)

Se il codice segnato non è quello che hai incluso, includi il codice segnato. Altrimenti penso che ci sia un sacco di spazio bianco che può essere giocato a golf.
Alex A.

1
@AlexA. lo spazio iniziale e le nuove righe a scopo di rientro non vengono conteggiati (e possono essere rimossi in modo sicuro). Una volta era permesso e nessuno si è lamentato.
edc65,

1
@ edc65 Il meta consenso ora è che il codice come punteggio dovrebbe essere fornito.
Alex A.

2
Bene, la maggior parte preferisce la versione illeggibile. Lascio ancora la versione leggibile qui, nel caso qualcuno voglia vedere cosa sta succedendo =)
flawr

2
È pratica comune fornire sia la sottomissione golfizzata (quella segnata) sia una versione non golfata, è sufficiente che sia inclusa quella segnata. ;)
Alex A.

7

Python 2, 151 140 138 byte

Implementazione lenta e ricorsiva della distanza Levenshtein basata su Wikipedia (Grazie a @Kenney per la rasatura di 11 caratteri e @ Sherlock9 per altri 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))

Fornire le risposte corrette per i casi di test presentati:

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

1
È possibile salvare 3-4 byte o giù di lì facendo qualcosa di simile if !n*m:return n if n else me altri 2 da return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ]).
Kenney,

Si risparmierebbe 2 byte usando f(m-1,n-1)-(s[m-1]==t[n-1])invece di f(m-1,n-1)+(s[m-1]!=t[n-1])-1.
Sherlock9,


5

JavaScript (ES6) 106 113 122

Modifica 16 byte salvati seguendo i suggerimenti di @Neil

Come una funzione anonima.

(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

Questa è un'implementazione golfizzata dell'algoritmo di Wagner-Fischer esattamente come descritto nell'articolo di Wikipedia collegato, nella sezione Iterative con due righe di matrice (anche se in realtà viene utilizzata solo una riga - array w )

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

Snippet di prova

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
Puoi usare [...[0,...t].keys()]invece? Salva 2 byte se puoi.
Neil,

1
@Neil che sembra brutto ma è più corto. Thx
edc65,

1
In realtà puoi salvare un altro byte, [...[,...t].keys()]funziona anche io credo.
Neil,

Sono riuscito a radere via un altro 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 grande, grazie ancora!
edc65,

4

Python 2, 118 byte

Un campo da golf di questa soluzione , ma non sembra che Willem sia attivo da un anno, quindi dovrò pubblicarlo da solo:

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

Prova su repl.it

Prende due stringhe e genera la distanza STDOUT( consentita da meta ). Si prega di commentare suggerimenti, sono sicuro che questo può essere ulteriormente giocato a golf.


È necessario avvolgere tutto in una funzione? Potresti usare due input()secondi o uno input().split()?
Sherlock9,

@ Sherlock9 L'ho provato, ma costa 1 byte in più , per quanto posso dire
FlipTack,

Bene, ho dimenticato che devi definire se tda qualche parte nel codice. Non importa. Buon lavoro: D
Sherlock9,

Non sono sicuro del motivo per cui Willem ha usato m or n. Puoi sostituirlo con m+n.
Arnauld,

3

AutoIt , 333 byte

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

Codice di prova di esempio:

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

i rendimenti

3
1
4
27

3

k4, 66 byte

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

Un impl noioso e fondamentalmente ungolfed dell'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

Scherzi a parte, 86 82 78 byte

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

Dump esadecimale:

2c232c23606b3b3b3bb03d222b6c229c4022b364293b295b5d6f71bd526975294064293b29405b
5d6f71bd5269752840294029405b5d6f71bd5269337d40293d592b6b6d229c40495269603bbb9f

Provalo online

(Si noti che il collegamento è a una versione diversa perché qualcosa sull'interprete online si interrompe con la nuova versione più corta, anche se funziona bene con l'interprete scaricabile.)

Si tratta dell'implementazione più semplice che consente seriamente la definizione ricorsiva. È molto lento perché non memorizza affatto. Forse il metodo tabulare potrebbe essere più breve (forse usando i registri come righe), ma sono abbastanza contento di questo, nonostante quante carenze di kludging-my-way-around-the-the-language contenga. Quello può usare

[]oq`<code>`Ri

come una corretta chiamata di funzione a due argomenti è stata una bella scoperta.

Spiegazione:

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

Mi piace il modo in cui il codice tenta di sfuggire al pre-elemento :)
mınxomaτ il

3

Python 3, 267 216 184 162 byte

Questa funzione calcola la distanza di Levenshtein usando un array 2 x len(word_2)+1di dimensioni.

Modifica: questo non si avvicina alla risposta Python 2 di Willem, ma qui c'è una risposta più giocosa con molti piccoli perfezionamenti qua e 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

Retina , 78 72 byte

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

Provalo online!

In un certo senso, questa è una soluzione regex pura in cui il risultato è il numero di posizioni da cui corrisponde la regex. Perché perché no ...

Attenzione, questo è super inefficiente. Il modo in cui funziona è che scarica l'ottimizzazione effettiva sul backtracker del motore regex, che semplicemente bruta forza tutti i possibili allineamenti, iniziando con il minor numero possibile di modifiche e permettendone uno in più fino a quando non è possibile abbinare le stringhe con aggiunte, eliminazioni e sostituzioni .

Per una soluzione leggermente più sensata, questa fa la corrispondenza solo una volta e non ha alcun aspetto negativo. Qui, il risultato è il numero di acquisizioni nel gruppo 2, a cui è possibile accedere match.Groups[2].Captures.Countin C #, ad esempio. È comunque orribilmente inefficiente.

Spiegazione

Sto spiegando la seconda versione sopra, perché concettualmente è un po 'più semplice (poiché è solo una singola partita regex). Ecco una versione non golfata che ho nominato i gruppi (o li ho resi non-catturanti) e ho aggiunto commenti. Ricorda che i componenti di un lookbehind devono essere letti da dietro a fronte, ma le alternative e i lookahead all'interno di questi devono essere letti da davanti a dietro. Si.

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

L'unica differenza rispetto alla versione a 72 byte è che possiamo eliminare il primo .+(e il secondo gruppo all'inizio) trovando posizioni alla fine in cui non ne abbiamo abbastanza <ops>e contiamo tutte quelle posizioni.


3

Haskell , 67 64 byte

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

Provalo online! Esempio di utilizzo: "turing" # "tarpit"rese 4.


Spiegazione (per la precedente versione di 67 byte)

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

Questa è una soluzione ricorsiva. Dato due stringhe ee f, confrontiamo prima i loro primi caratteri ae b. Se sono uguali, allora la distanza di Levenshtein di eed fè uguale alla distanza di Levenshtein di re s, il resto di ee fdopo aver rimosso i primi caratteri. Altrimenti, ao bdeve essere rimosso o uno è sostituito dall'altro. [r#f,e#s,r#s]calcola in modo ricorsivo il Levenshtein per quei tre casi, minimumsceglie il più piccolo e 1viene aggiunto per tenere conto dell'operazione di rimozione o sostituzione necessaria.

Se una delle stringhe è vuota, noi e la seconda riga. In questo caso, la distanza è solo la lunghezza della stringa non vuota, o equivalentemente la lunghezza di entrambe le stringhe concatenate insieme.


1
Caspita, questa è davvero una buona soluzione, davvero elegante e breve.
ggPeti,

3

Python 3 , 105 94 93 byte

-11 byte di 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)

Versione golfizzata dell'implementazione più breve su Wikibooks .

Provalo online!


Bella soluzione. È l=necessario includere e contare perché la funzione è ricorsiva. È possibile combinare i casi di basi in if b>""<a else len(a+b).
xnor

Bel gioco con gli operatori, grazie!
movatica,

2

Haskell, 136 byte

Chiama e. Un po 'lento antidisestablishmentarianismecc.

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 byte

Provalo qui!

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

L'ho aggiunto ieri, ma ho visto questa sfida oggi, cioè proprio ora. Tuttavia, questa risposta non è competitiva.

In una versione più recente:

~Li

accetta un secondo input implicito.


" Il tuo codice deve essere un programma o una funzione. Non deve essere una funzione con nome, ma non può essere una funzione integrata che calcola direttamente la distanza di Levenshtein . Sono consentiti altri incorporati. "
Kevin Cruijssen

Ah, non ti ho visto menzionato che non è in competizione .. Meglio metterlo nel titolo, o aggiungere un programma / funzione valido senza incorporato.
Kevin Cruijssen,

2

GNU Prolog, 133 byte

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

Prende una tupla come argomento. Esempio di utilizzo:

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

D = 4

yes

mspecifica che Bè Adirettamente o con il suo primo carattere rimosso. dutilizza mcome subroutine per calcolare una distanza di modifica tra gli elementi tupla (ovvero la distanza di una serie di modifiche che converte l'una nell'altra). Quindi lè un trucco standard per trovare il minimo di d(prendi una distanza arbitraria, quindi prendi una distanza arbitraria più piccola, ripeti fino a quando non puoi andare più piccolo).


1

Perl, 168 166 163 byte

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)

Implementazione ricorsiva. Salva in a file.pled esegui come 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)


Le altre due implementazioni sono entrambe più lunghe (matrice completa: 237 byte, due iterativi a una riga: 187).

  • aggiornamento 166 : ometti() nella chiamata l.
  • aggiornamento 163 : eliminare returnabusando doin trinario.


0

C, 192 byte

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

dettagliata

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

più leggibile:

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 byte

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

Ho finito per fare questo per risolvere altre sfide che coinvolgono LD.

Spiegazione del codice con commenti.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.