Distância Levenshtein

39

Embora existam muitas perguntas sobre distância de edição, como esta , não há uma pergunta simples para escrever um programa que calcule a distância de Levenshtein.

Alguma exposição

A distância de edição de Levenshtein entre duas strings é o número mínimo possível de inserções, exclusões ou substituições para converter uma palavra em outra palavra. Nesse caso, cada inserção, exclusão e substituição tem um custo de 1.

Por exemplo, a distância entre rolle rollingé 3, porque as exclusões custam 1 e precisamos excluir 3 caracteres. A distância entre tolle tallé 1, porque as substituições custam 1.

Regras

  • A entrada será duas strings. Você pode assumir que as seqüências de caracteres são minúsculas, contêm apenas letras, não estão vazias e têm no máximo 100 caracteres.
  • A saída será a distância mínima de edição de Levenshtein das duas seqüências, conforme definido acima.
  • Seu código deve ser um programa ou uma função. Ele não precisa ser uma função nomeada, mas não pode ser uma função interna que calcula diretamente a distância de Levenshtein. Outros embutidos são permitidos.
  • Isso é código de golfe, então a resposta mais curta vence.

Alguns exemplos

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

Como sempre, se o problema não estiver claro, entre em contato. Boa sorte e bom golfe!

Catálogo

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>

Sherlock9
fonte

Respostas:

8

Pitão, 34 bytes

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

Demonstração

Isso não é particularmente bom para o golfe e é muito lento. Ele não pode lidar com nada além de duas alterações em um período de tempo razoável.

isaacg
fonte
3
Mas funciona, e é isso que conta. : 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 é uma implementação direta desta fórmula:

insira a descrição da imagem aqui

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)
flawr
fonte
Se o código marcado não for o que você incluiu, inclua o código marcado. Caso contrário, acho que há muito espaço em branco que pode ser jogado fora.
Alex A.
11
@AlexA. o espaço à esquerda e as novas linhas para fins de indentação não são contados (e podem ser removidos com segurança). Uma vez isso foi permitido e ninguém reclamou.
Edc65
11
@ edc65 O meta consenso agora é que o código conforme pontuado deve ser fornecido.
Alex A.
2
Bem, então, a maioria prefere a versão ilegível. Eu ainda deixar a versão legível aqui, no caso de alguém pode querer ver o que está realmente acontecendo =)
flawr
2
É prática comum fornecer tanto a finalização do jogo (a que foi pontuada) quanto a versão não-finalizada, apenas exigimos que a finalização seja incluída. ;)
Alex A.
7

Python 2, 151 140 138 bytes

Implementação recursiva lenta da distância de Levenshtein com base na Wikipedia (obrigado a @Kenney pelo corte de 11 caracteres e @ Sherlock9 por outros 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))

Fornecendo as respostas corretas para os casos de teste apresentados:

assert l("tar", "tarp") == 1
assert l("turing", "tarpit") == 4
assert l("antidisestablishmentarianism", "bulb") == 27        
assert l("atoll", "bowl") == 3
Willem
fonte
11
Você pode economizar de 3 a 4 bytes, fazendo algo como if !n*m:return n if n else m, e outros 2 por return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ]).
22615 Kenney
Você salvaria 2 bytes usando em f(m-1,n-1)-(s[m-1]==t[n-1])vez de f(m-1,n-1)+(s[m-1]!=t[n-1])-1.
Sherlock9
Golfed off 20 chars: codegolf.stackexchange.com/a/102910/60919
FlipTack
5

JavaScript (ES6) 106 113 122

Edite 16 bytes salvos seguindo as sugestões @Neil

Como uma função 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 é uma implementação em golfe do algoritmo Wagner-Fischer exatamente como descrito no artigo da Wikipedia, na seção Iterativa com duas linhas da matriz (mesmo que, de fato, apenas 1 linha seja usada - matriz w )

Menos golfe

(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 de teste

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>

edc65
fonte
11
Você pode usar [...[0,...t].keys()]? Salva 2 bytes, se puder.
Neil
11
@ Neil que parece feio, mas é mais curto. Thx
edc65
11
Na verdade, você pode salvar outro byte, [...[,...t].keys()]também funciona , eu acho.
Neil
Consegui remover outro 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 ótimo, obrigado novamente!
edc65
4

Python 2, 118 bytes

Golfe dessa solução , mas não parece que Willem esteja há um ano, então terei que publicá-la eu mesmo:

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

Experimente repl.it

Pega duas strings e gera a distância até STDOUT( permitida por meta ). Por favor, comente sugestões, tenho certeza de que isso pode ser jogado ainda mais.

FlipTack
fonte
É necessário agrupar tudo em uma função? Você poderia usar dois input()s ou um input().split()?
Sherlock9
@ Sherlock9 Tentei fazer isso, mas custa 1 byte extra , tanto quanto eu posso dizer
FlipTack
Certo, esqueci que você precisa definir se tem algum lugar do código. Deixa pra lá. Bom trabalho: D
Sherlock9
Não sei por que Willem usou m or n. Você pode substituí-lo por 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 teste de amostra:

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

rendimentos

3
1
4
27
mınxomaτ
fonte
3

k4, 66 bytes

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

Um implemento chato e basicamente não-destruído pelo 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
Aaron Davies
fonte
3

Sério, 86 82 78 bytes

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

Hex Dump:

2c232c23606b3b3b3bb03d222b6c229c4022b364293b295b5d6f71bd526975294064293b29405b
5d6f71bd5269752840294029405b5d6f71bd5269337d40293d592b6b6d229c40495269603bbb9f

Experimente Online

(Observe que o link é para uma versão diferente porque algo sobre o intérprete on-line quebra com a nova versão mais curta, mesmo que funcione bem com o intérprete para download.)

É a implementação mais direta. Seriamente permite a definição recursiva. É muito lento porque não realiza nenhuma memorização. Talvez o método tabular possa ser mais curto (talvez usando os registradores como linhas), mas estou bastante satisfeito com isso, apesar das limitações que ele contém. Aquele pode usar

[]oq`<code>`Ri

como uma chamada de função adequada com dois argumentos foi uma boa descoberta.

Explicação:

,#,#                             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
quintopia
fonte
Eu gosto de como as tentativas de código para escapar do pré-elemento :)
mınxomaτ
3

Python 3, 267 216 184 162 bytes

Esta função calcula a distância de Levenshtein usando uma matriz 2 x len(word_2)+1com tamanho.

Edit: Isso não chega nem perto da resposta do Willem em Python 2, mas aqui está uma resposta mais eficiente, com muitos pequenos refinamentos aqui e ali.

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]
Sherlock9
fonte
3

Retina , 78 72 bytes

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

Experimente online!

De certa forma, essa é uma solução de regex pura em que o resultado é o número de posições das quais o regex corresponde. Porque porque não ...

Aviso justo, isso é super ineficiente. A maneira como isso funciona é que ele transfere a otimização real para o backtracker do mecanismo regex, que simplesmente força bruta em todos os alinhamentos possíveis, começando com o mínimo de alterações possível e permitindo mais um até que seja possível combinar as strings com adições, exclusões e substituições .

Para uma solução um pouco mais sensata, este faz a correspondência apenas uma vez e não tem nenhuma busca negativa. Aqui, o resultado é o número de capturas no grupo 2, às quais você pode acessar match.Groups[2].Captures.Countem C #, por exemplo. Ainda é terrivelmente ineficiente.

Explicação

Estou explicando a segunda versão acima, porque é conceitualmente um pouco mais fácil (pois é apenas uma única correspondência de regex). Aqui está uma versão não destruída que chamei os grupos (ou os fiz de não capturar) e adicionei comentários. Lembre-se de que os componentes em um lookbehind devem ser lidos de trás para a frente, mas as alternativas e os lookaheads dentro deles devem ser lidos de frente para trás. Sim.

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

A única diferença para a versão de 72 bytes é que podemos abandonar a liderança .+(e o segundo grupo no começo) encontrando posições no final em que não temos o suficiente <ops>e contando todas essas posições.

Martin Ender
fonte
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

Experimente online! Exemplo de uso: "turing" # "tarpit"rendimentos 4.


Explicação (para a versão 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 é uma solução recursiva. Dadas duas cadeias ee f, primeiro comparamos seus primeiros caracteres ae b. Se forem iguais, a distância de Levenshtein de ee fé a mesma que distância de Levenshtein de re s, o restante de ee fdepois de remover os primeiros caracteres. Caso contrário, um aou bprecisa ser removido ou um é substituído pelo outro. [r#f,e#s,r#s]calcula recursivamente o Levenshtein para esses três casos, minimumescolhe o menor e 1é adicionado para explicar a operação de remoção ou substituição necessária.

Se uma das strings estiver vazia, nós subimos na segunda linha. Nesse caso, a distância é apenas o comprimento da sequência não vazia ou, equivalentemente, o comprimento de ambas as seqüências concatenadas.

Laikoni
fonte
11
Uau, esta é uma solução muito boa, realmente elegante e curta.
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)

Versão mais curta da implementação mais curta no Wikilivros .

Experimente online!

movatica
fonte
Ótima solução. É l=necessário incluir e contar porque a função é recursiva. Você pode combinar os casos de bases em if b>""<a else len(a+b).
xnor 27/04
Bom jogo com os operadores, obrigado!
movatica 27/04
2

Haskell, 136 bytes

Ligue e. Pouco 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)]
Leif Willerts
fonte
2

Jolf, 4 bytes

Experimente aqui!

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

Eu adicionei isso ontem, mas vi esse desafio hoje, ou seja, agora. Ainda assim, esta resposta não é competitiva.

Em uma versão mais recente:

~Li

recebe segunda entrada implícita.

Conor O'Brien
fonte
"O seu código deve um programa ou uma função Não precisa ser uma função chamada, mas ele não pode ser um built-in função que calcula diretamente a distância Levenshtein Outros built-ins são permitidos.. "
Kevin Cruijssen
Ah, você não mencionou que não é concorrente? É melhor colocá-lo no título ou adicionar um programa / função válido sem o recurso embutido.
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 uma tupla como argumento. Exemplo de uso:

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

D = 4

yes

mespecifica que Bé Adiretamente ou com seu primeiro caractere removido. dusa mcomo uma sub-rotina para calcular uma distância de edição entre os elementos da tupla (ou seja, a distância de uma série de edições que converte uma na outra). Então, lé um truque padrão para encontrar o mínimo de d(você percorre uma distância arbitrária e, em seguida, percorre uma distância menor arbitrária, repita até não poder diminuir).


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

Implementação recursiva. Salve em file.ple execute 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)


As outras duas implementações são mais longas (matriz completa: 237 bytes, duas iterativas de uma linha: 187).

  • atualização 166 : omitir ()na chamada l.
  • atualização 163 : elimine returnabusando doem trinary.
Kenney
fonte
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;}
---------

Detalhado

#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;
}
Khaled.K
fonte
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;}

mais legível:

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

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

Acabei fazendo isso para resolver outros desafios envolvendo LDs.

Código de explicação com comentários.

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