Расстояние Левенштейна


39

Несмотря на то, что существует много вопросов о расстоянии редактирования, таких как этот , нет простого вопроса, чтобы написать программу, которая рассчитывает расстояние Левенштейна.

Некоторая экспозиция

Расстояние редактирования Левенштейна между двумя строками - это минимально возможное количество вставок, удалений или замен для преобразования одного слова в другое. В этом случае каждая вставка, удаление и замена имеют стоимость 1.

Например, расстояние между rollи rollingравно 3, потому что удаление стоит 1, а нам нужно удалить 3 символа. Расстояние между tollи tallравно 1, потому что замены стоят 1.

правила

  • На входе будут две строки. Вы можете предположить, что строки строчные, содержат только буквы, не являются пустыми и имеют длину не более 100 символов.
  • Выходными данными будет минимальное расстояние редактирования Левенштейна для двух строк, как определено выше.
  • Ваш код должен быть программой или функцией. Это не обязательно должна быть именованная функция, но она не может быть встроенной функцией, которая напрямую вычисляет расстояние Левенштейна. Другие встроенные модули разрешены.
  • Это код гольф, поэтому самый короткий ответ выигрывает.

Некоторые примеры

>>> 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
Конор О'Брайен

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)

Если набранный вами код не соответствует тому, что вы включили, включите набранный код. В противном случае я думаю, что есть много пробелов, которые можно вывести из игры.
Алекс А.

1
@AlexA. начальные пробелы и символы новой строки для отступов не учитываются (и могут быть безопасно удалены). Когда-то это было разрешено, и никто не жаловался.
edc65

1
@ edc65 В настоящее время мета-консенсус заключается в том, что должен быть предоставлен код с оценкой .
Алекс А.

2
Ну, тогда большинство предпочитает нечитаемую версию. Я до сих пор оставляю читаемую версию здесь, на случай, если кто-нибудь захочет увидеть, что на самом деле происходит =)
flawr

2
Обычной практикой является предоставление как гольфовой заявки (той, которая забита), так и неголевой версии, мы просто требуем, чтобы забитая была включена. ;)
Алекс А.

7

Python 2, 151 140 138 байт

Медленная рекурсивная реализация расстояния Левенштейна, основанная на Википедии (спасибо @Kenney за бритье 11 символов и @ 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, и еще 2 байта return 1+min([ f(..), f(..), f(..) - (s[..] == t[..]) ]).
Кенни

Вы бы сэкономили 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

Это сложная реализация алгоритма Вагнера – Фишера, точно такая же, как описанная в связанной статье в Википедии, в разделе Итеративный с двумя строками матрицы (даже если на самом деле используется только 1 строка - массив w )

Меньше гольфа

(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 байта, если можете.
Нил

1
@ Нил выглядит ужасно, но он короче. Thx
edc65

1
На самом деле вы можете сохранить другой байт, [...[,...t].keys()]я думаю , тоже работает.
Нил

Мне удалось сбрить еще один байт, используя [...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)
Нейл

@ Нейл здорово, спасибо еще раз!
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( разрешено мета ). Пожалуйста, прокомментируйте предложения, я уверен, что это может быть в дальнейшем.


Нужно ли оборачивать все в функцию? Не могли бы вы использовать два input()с или input().split()?
Sherlock9

@ Sherlock9 Я попробовал это, но насколько я могу судить , это стоит на 1 байт больше
FlipTack

Правильно, я забыл, что нужно определять sи tгде-то в коде. Не берите в голову. Хорошая работа: D
Sherlock9

Я не уверен, почему Виллем использовал m or n. Вы можете заменить его на m+n.
Арно

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

Скучный и в основном ungolfed impl алгоритма. Напр .:

  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-round-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 байта

Эта функция вычисляет расстояние Левенштейна, используя массив, который имеет 2 x len(word_2)+1размер.

Изменить: Это не близко к ответу Виллема на Python 2, но вот более гольф ответ с множеством небольших уточнений здесь и там.

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, который просто перебирает все возможные выравнивания, начиная с как можно меньшего количества изменений и допуская еще одно, пока не станет возможным сопоставить строки с добавлениями, удалениями и заменами. ,

Для чуть более разумного решения, оно выполняет сопоставление только один раз и не имеет негативных последствий. Здесь результатом является количество перехватов в группе 2, к которым вы можете получить доступ match.Groups[2].Captures.Count, например, в C #. Это все еще ужасно неэффективно все же.

объяснение

Я объясняю вторую версию выше, потому что концептуально это немного проще (так как это всего лишь одно совпадение с регулярным выражением). Вот незагрязненная версия, я назвал группы (или сделал их не захватывающими) и добавил комментарии. Помните, что компоненты в представлении сзади должны читаться спереди назад, но альтернативы и подсказки внутри них должны читаться спереди назад. Да.

.+                      # 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. Если они равны, то расстояние Левенштейна eи fравно расстоянию Левенштейна rи s, остаток от eи fпосле удаления первых символов. В противном случае либо aлибо bнеобходимо удалить, либо один заменить другим. [r#f,e#s,r#s]Рекурсивно вычисляет Левенштейна для этих трех случаев, 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)

Гольф версия самой короткой реализации на Wikibooks .

Попробуйте онлайн!


Хорошее решение. В l=должны быть включены и подсчитывали , потому что функция является рекурсивной. Вы можете объединить базовые случаи в if b>""<a else len(a+b).
xnor

Приятно играть с операторами, спасибо!
Моватика

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

Джольф, 4 байта

Попробуй это здесь!

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

Я добавил, что встроил вчера, но видел этот вызов сегодня, то есть только сейчас. Тем не менее, этот ответ неконкурентен.

В более новой версии:

~Li

принимает неявный второй вход.


«Ваш код должен программа или функция Она не должна быть названа функция, но она не может быть встроенной функцией , которая непосредственно вычисляет расстояние Левенштейна Другие встроенные модули разрешены.. »
Кевин Cruijssen

Ах, я не видел, что вы упомянули, что он неконкурентный. Лучше поместите его в заголовок или добавьте действующую программу / функцию без встроенной функции.
Кевин Круйссен

2

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

Принимает кортеж в качестве аргумента. Пример использования:

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

D = 4

yes

mуказывает , что Bявляется Aлибо непосредственно , либо с ее первый символ удаляется. dиспользование в mкачестве подпрограммы для вычисления в расстояние редактирования между элементами кортежей (т.е. расстояние от ряда изменений , которые обращенные друг в друга). Тогда lэто стандартный трюк для нахождения минимума d(вы берете произвольное расстояние, затем выбираете произвольно меньшее расстояние, повторяйте, пока вы не можете уменьшиться).


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.plи запустить как 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)


Две другие реализации являются более длинными (полная матрица: 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]

Я сделал это, чтобы решить другие проблемы, связанные с LD.

Объяснение кода с комментариями.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.