ระยะทาง Levenshtein


40

ในขณะที่มีคำถามระยะทางแก้ไขมากมายเช่นคำถามนี้ไม่มีคำถามง่าย ๆ ในการเขียนโปรแกรมที่คำนวณระยะทางของ Levenshtein

นิทรรศการบางอย่าง

ระยะทางแก้ไขของ Levenshtein ระหว่างสองสายคือจำนวนที่น้อยที่สุดที่เป็นไปได้ของการแทรกการลบหรือการแทนที่เพื่อแปลงหนึ่งคำเป็นคำอื่น ในกรณีนี้การแทรกการลบและการแทนที่แต่ละรายการมีค่า 1

ตัวอย่างเช่นระยะห่างระหว่างrollและrollingคือ 3 เนื่องจากการลบมีค่าใช้จ่าย 1 และเราต้องลบ 3 ตัวอักษร ระยะห่างระหว่างtollและtallคือ 1 เนื่องจากการทดแทนมีค่าใช้จ่าย 1

กฎระเบียบ

  • อินพุตจะเป็นสองสตริง คุณอาจสันนิษฐานว่าสตริงเป็นตัวพิมพ์เล็กประกอบด้วยตัวอักษรเท่านั้นไม่ว่างเปล่าและมีความยาวสูงสุด 100 อักขระ
  • เอาต์พุตจะเป็นระยะทางแก้ไขขั้นต่ำของ Levenshtein ของทั้งสองสายดังที่กำหนดไว้ข้างต้น
  • รหัสของคุณต้องเป็นโปรแกรมหรือฟังก์ชั่น มันไม่จำเป็นต้องเป็นฟังก์ชั่นที่มีชื่อ แต่มันไม่สามารถเป็นฟังก์ชั่นในตัวที่คำนวณระยะทาง Levenshtein ได้โดยตรง บิวด์อินอื่น ๆ ได้รับอนุญาต
  • นี่คือรหัสกอล์ฟดังนั้นคำตอบที่สั้นที่สุดชนะ

ตัวอย่างบางส่วน

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

เช่นเคยหากปัญหายังไม่ชัดเจนโปรดแจ้งให้เราทราบ ขอให้โชคดีและการเล่นกอล์ฟที่ดี!

แค็ตตาล็อก

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>

คำตอบ:


8

Pyth, 34 ไบต์

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

สาธิต

นี่ไม่ใช่การเล่นกอล์ฟที่ดีและช้ามาก ไม่สามารถจัดการกับการเปลี่ยนแปลง 2 อย่างที่ผ่านมาในช่วงเวลาที่เหมาะสม


3
แต่มันได้ผลและนั่นคือสิ่งที่สำคัญ : P
Conor O'Brien

10

Matlab, 177 163 ไบต์

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)

นี่คือการใช้งานสูตรนี้อย่างตรงไปตรงมา:

ป้อนคำอธิบายรูปภาพที่นี่

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)

หากรหัสที่ได้คะแนนไม่ใช่สิ่งที่คุณได้รวมไว้โปรดระบุรหัสที่ได้ มิฉะนั้นผมคิดว่ามีเป็นจำนวนมากของช่องว่างที่สามารถแข็งแรงเล่นกอล์ฟออก
Alex A.

1
@AlexA พื้นที่ชั้นนำและบรรทัดใหม่เพื่อจุดประสงค์ในการเยื้องจะไม่ถูกนับ (และสามารถลบออกได้อย่างปลอดภัย) เมื่อถึงเวลานี้จะได้รับอนุญาตและไม่มีใครบ่น
edc65

1
@ edc65 ฉันทามติเมตาในขณะนี้คือควรให้รหัสตามคะแนน
Alex A.

2
ถ้าอย่างนั้นส่วนใหญ่ชอบเวอร์ชั่นที่อ่านไม่ได้ ฉันยังคงปล่อยให้รุ่นที่สามารถอ่านได้ที่นี่ในกรณีที่คนอาจต้องการที่จะเห็นสิ่งที่เป็นจริงที่เกิดขึ้น =)
flawr

2
มันเป็นเรื่องธรรมดาที่จะให้ทั้งการยอมแพ้ของนักกอล์ฟ (ผู้ที่ได้คะแนน) และเวอร์ชั่นที่ไม่ดีเราก็แค่ต้องการให้มีการรวมคะแนน ;)
Alex A.

7

Python 2, 151 140 138 ไบต์

การใช้งานระยะไกลแบบซ้ำ ๆ ของ Levenshtein ช้าตามWikipedia (ขอบคุณ @Kenney สำหรับการโกน 11 chars และ @ Sherlock9 อีก 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))

ให้คำตอบที่ถูกต้องสำหรับกรณีทดสอบที่นำเสนอ:

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

1
คุณอาจบันทึก 3-4 ไบต์หรือดังนั้นด้วยการทำสิ่งที่ชอบif !n*m:return n if n else mและอีก return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ])2
Kenney

คุณจะประหยัด 2 ไบต์โดยใช้แทนf(m-1,n-1)-(s[m-1]==t[n-1]) f(m-1,n-1)+(s[m-1]!=t[n-1])-1
Sherlock9

ลงสนาม 20 ตัวอักษร: codegolf.stackexchange.com/a/102910/60919
FlipTack

5

JavaScript (ES6) 106 113 122

แก้ไข 16 ไบต์ที่บันทึกตามคำแนะนำ @Neil

เป็นฟังก์ชั่นนิรนาม

(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

นี่เป็นการใช้งานอัลกอริทึมของแว็กเนอร์ - ฟิสเชอร์แบบ Golfed ตามที่อธิบายไว้ในบทความวิกิพีเดียที่เชื่อมโยงในส่วนซ้ำกับแถวเมทริกซ์สองแถว (แม้ว่าจริงแล้วจะใช้เพียง 1 แถว - อาร์เรย์w )

น้อย golfed

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

ตัวอย่างการทดสอบ

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
คุณสามารถใช้[...[0,...t].keys()]แทนได้หรือไม่? บันทึก 2 ไบต์ถ้าคุณทำได้
Neil

1
@ นีลที่ดูน่าเกลียด แต่มันสั้นกว่า ขอบคุณ
edc65

1
ที่จริงคุณสามารถบันทึกไบต์อื่น[...[,...t].keys()]ได้เช่นกันฉันก็คิดเช่นกัน
Neil

ฉันจัดการเพื่อกำจัดไบต์อื่นโดยใช้[...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

@ ดีมากขอบคุณอีกครั้ง!
edc65

4

Python 2, 118 ไบต์

สนามกอล์ฟของวิธีแก้ปัญหานี้แต่ดูเหมือนว่าวิลเล็มจะไม่ได้เปิดเป็นเวลาหนึ่งปีดังนั้นฉันจะต้องโพสต์ด้วยตัวเอง:

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

ลองใช้ repl.it

ใช้สองสตริงและส่งออกระยะทางไปที่STDOUT( อนุญาตโดยเมตาดาต้า ) กรุณาแสดงความคิดเห็นข้อเสนอแนะฉันแน่ใจว่านี้สามารถ golfed เพิ่มเติม


จำเป็นต้องห่อทุกอย่างในฟังก์ชั่นหรือไม่? คุณสามารถใช้สองตัวinput()หรืออันinput().split()?
Sherlock9

@ Sherlock9 ฉันลองมัน แต่มันมีราคาเพิ่มขึ้น 1 ไบต์เท่าที่ฉันจะบอกได้
FlipTack

ใช่ฉันลืมว่าคุณต้องกำหนดsและtบางแห่งในรหัส ไม่เป็นไร. งานที่ดี: D
Sherlock9

m or nฉันไม่แน่ใจว่าทำไมวิลเล็มใช้ m+nคุณสามารถแทนที่ด้วย
Arnauld

3

AutoIt 333 ไบต์

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

ตัวอย่างรหัสทดสอบ:

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

อัตราผลตอบแทน

3
1
4
27

3

k4, 66 ไบต์

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

ความหมายที่น่าเบื่อและไม่มีมารยาทของ algo อดีต .:

  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

อย่างจริงจัง86 82 78 ไบต์

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

ฐานสิบหก:

2c232c23606b3b3b3bb03d222b6c229c4022b364293b295b5d6f71bd526975294064293b29405b
5d6f71bd5269752840294029405b5d6f71bd5269337d40293d592b6b6d229c40495269603bbb9f

ลองใช้ออนไลน์

(โปรดทราบว่าลิงก์นั้นเป็นรุ่นที่แตกต่างกันเพราะมีบางอย่างเกี่ยวกับล่ามออนไลน์ที่มีเวอร์ชันใหม่ที่สั้นกว่าแม้ว่ามันจะทำงานได้ดีกับล่ามที่ดาวน์โหลดได้)

มันเกี่ยวกับการใช้งานที่ตรงไปตรงมาที่สุดช่วยให้คำจำกัดความซ้ำอย่างจริงจัง ช้ามากเพราะไม่มีการบันทึกความจำเลย บางทีวิธีการแบบตารางอาจสั้นกว่านี้ (อาจใช้รีจิสเตอร์เป็นแถว) แต่ฉันก็ค่อนข้างพอใจกับสิ่งนี้ถึงแม้จะมีข้อบกพร่องของ kludging-my-way-around-the-language ที่หนึ่งสามารถใช้

[]oq`<code>`Ri

ในฐานะที่เป็นฟังก์ชั่นการเรียกสองข้อโต้แย้งที่เหมาะสมก็ค่อนข้างพบว่าดี

คำอธิบาย:

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

ฉันชอบวิธีที่รหัสพยายามที่จะหลบหนีองค์ประกอบก่อนหน้า :)
mınxomaτ

3

Python 3, 267 216 184 162 ไบต์

ฟังก์ชันนี้คำนวณระยะทาง Levenshtein โดยใช้อาร์เรย์ที่มี2 x len(word_2)+1ขนาด

แก้ไข:นี่ไม่ใกล้กับคำตอบ Python 2 ของ Willem แต่นี่เป็นคำตอบที่ยากขึ้นสำหรับการเล่นกอล์ฟที่มีการปรับแต่งเล็กน้อยที่นี่และที่นั่น

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

เรติน่า , 78 72 ไบต์

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

ลองออนไลน์!

ในทางนี้เป็นทางออกที่บริสุทธิ์ regex ซึ่งผลที่ได้คือจำนวนของตำแหน่งที่ regex ตรงกับ เพราะทำไมไม่ ...

คำเตือนยุติธรรมนี้ไม่มีประสิทธิภาพมาก วิธีการทำงานนี้คือการลดการเพิ่มประสิทธิภาพที่แท้จริงให้กับ backtracker ของ regex engine ซึ่งเพียงแค่เดรัจฉานบังคับแนวร่วมทั้งหมดที่เป็นไปได้เริ่มต้นด้วยการปรับเปลี่ยนน้อยที่สุดเท่าที่เป็นไปได้และให้อีกหนึ่งจนกว่าจะเป็นไปได้ที่จะจับคู่ .

สำหรับวิธีแก้ปัญหาที่สมเหตุสมผลมากขึ้นอันนี้จะจับคู่เพียงครั้งเดียวและไม่มีลักษณะเชิงลบใด ๆ ที่นี่ผลลัพธ์คือจำนวนการดักจับในกลุ่ม2ซึ่งคุณสามารถเข้าถึงด้วยmatch.Groups[2].Captures.Countใน C # เป็นต้น มันยังคงไม่มีประสิทธิภาพอย่างน่ากลัวแม้ว่า

คำอธิบาย

ฉันกำลังอธิบายรุ่นที่สองข้างต้นเพราะมันเป็นแนวคิดที่ง่ายขึ้นเล็กน้อย (เนื่องจากเป็นเพียงการจับคู่ regex เดียว) นี่คือเวอร์ชันที่ไม่อัปโหลดฉันได้ตั้งชื่อกลุ่ม (หรือทำให้ไม่จับภาพ) และเพิ่มความคิดเห็น โปรดจำไว้ว่าส่วนประกอบใน lookbehind ควรอ่านจากด้านหลังไปข้างหน้า แต่ตัวเลือกและ lookaheads ภายในนั้นควรอ่านจากด้านหน้าไปด้านหลัง ใช่.

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

ความแตกต่างเพียงอย่างเดียวกับรุ่น 72 ไบต์คือเราสามารถดรอปชั้นนำ.+(และกลุ่มที่สองในตอนเริ่มต้น) โดยการค้นหาตำแหน่งท้ายที่สุดที่เรามีไม่เพียงพอ<ops>และนับตำแหน่งเหล่านั้นทั้งหมด


3

Haskell , 67 64 ไบต์

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

ลองออนไลน์! ตัวอย่างการใช้งาน: อัตราผลตอบแทน"turing" # "tarpit"4


คำอธิบาย (สำหรับรุ่น 67 ไบต์ก่อนหน้า)

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

นี่เป็นโซลูชันแบบเรียกซ้ำ ได้รับสองสตริงeและfครั้งแรกที่เราเปรียบเทียบตัวอักษรแรกของพวกเขาและa bหากพวกเขามีค่าเท่ากันระยะทางของ Levenshtein eและfเท่ากับระยะทางของ Levenshtein rและsส่วนที่เหลือของeและfหลังจากลบอักขระตัวแรก ไม่เช่นนั้นaหรือbต้องถูกลบออกหรืออีกอันหนึ่งถูกแทนที่โดยอันอื่น [r#f,e#s,r#s]คำนวณ Levenshtein ซ้ำสำหรับสามกรณีนั้นminimumเลือกอันที่เล็กที่สุดและ1ถูกเพิ่มเข้าบัญชีสำหรับการดำเนินการลบหรือเปลี่ยนที่จำเป็น

หากหนึ่งในสตริงว่างเปล่าเราและขึ้นบนบรรทัดที่สอง ในกรณีนี้ระยะทางเป็นเพียงความยาวของสตริงที่ไม่ว่างเปล่าหรือเท่ากับความยาวของสตริงทั้งสองที่ต่อกันเข้าด้วยกัน


1
ว้าวนี่เป็นทางออกที่ดีอย่างจริงจังสง่างามและสั้น
ggPeti

3

Python 3 , 105 94 93 ไบต์

-11 ไบต์โดย 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)

รุ่นแข็งแรงเล่นกอล์ฟของการใช้งานที่สั้นที่สุดในวิกิพีเดีย

ลองออนไลน์!


ทางออกที่ดี l=ความต้องการที่จะรวมและนับเพราะฟังก์ชั่นคือ recursive คุณสามารถรวมเคสพื้นฐานเข้าif b>""<a else len(a+b)ด้วยกัน
xnor

เล่นได้ดีกับโอเปอเรเตอร์ขอบคุณ!
movatica

2

Haskell, 136 ไบต์

โทรe. บิตช้าลงantidisestablishmentarianismเป็นต้น

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 ไบต์

ลองที่นี่!

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

ฉันเพิ่มที่สร้างขึ้นเมื่อวานนี้ แต่เห็นความท้าทายในวันนี้คือเพียงแค่ตอนนี้ ถึงกระนั้นคำตอบนี้ยังไม่สมบูรณ์

ในรุ่นที่ใหม่กว่า:

~Li

ใช้อินพุตที่สองโดยนัย


" โค้ดของคุณต้องเป็นโปรแกรมหรือฟังก์ชั่นมันไม่จำเป็นต้องเป็นฟังก์ชั่นที่มีชื่อ แต่มันไม่สามารถเป็นฟังก์ชั่นในตัวที่คำนวณระยะทาง Levenshtein ได้โดยตรงอนุญาตให้มีบิวด์อินอื่น "
Kevin Cruijssen

อ่าไม่เห็นว่าคุณพูดถึงมันไม่ใช่การแข่งขัน .. วางมันไว้ในชื่อเรื่องหรือเพิ่มโปรแกรม / ฟังก์ชั่นที่ถูกต้องโดยไม่ต้องสร้างขึ้น
Kevin Cruijssen

2

อารัมภบท GNU, 133 ไบต์

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

รับ tuple เป็นอาร์กิวเมนต์ ตัวอย่างการใช้งาน:

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

D = 4

yes

mระบุว่าBเป็นAโดยตรงหรือมีการลบตัวอักษรตัวแรก dใช้mเป็นย่อยในการคำนวณแก้ไขระยะห่างระหว่างองค์ประกอบ tuple (เช่นระยะของชุดของการแก้ไขที่แปลงหนึ่งไปยังที่อื่น ๆ ) จากนั้นเป็นเคล็ดลับมาตรฐานสำหรับการค้นหาขั้นต่ำ(คุณใช้ระยะทางตามอำเภอใจจากนั้นใช้ระยะทางที่เล็กลงโดยทำซ้ำจนกว่าคุณจะไม่เล็กลง)ld


1

Perl, 168 166 163 ไบต์

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)

การใช้งานแบบเรียกซ้ำ บันทึกในและทำงานเป็นfile.plperl 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)


อีกสองการใช้งานอีกต่อไปมีความยาว (เมทริกซ์เต็ม: 237 ไบต์สองซ้ำหนึ่งแถว: 187)

  • ปรับปรุง 166 : งดในการโทร()l
  • อัปเดต 163 : กำจัดreturnโดยใช้doในทางที่ผิด


0

C, 192 ไบต์

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

รายละเอียด

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

อ่านได้มากขึ้น:

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 ไบต์

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

ฉันลงเอยด้วยการทำสิ่งนี้เพื่อแก้ปัญหาความท้าทายอื่นที่เกี่ยวข้องกับแอลดี

คำอธิบายรหัสที่มีความคิดเห็น

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.