O problema da distância Levenshtein (editar distância)

Google Translate Icon

A distância Levenshtein (ou distância de edição) é uma maneira de quantificar a diferença entre duas strings contando o número mínimo de operações necessárias para transformar uma string na outra.

A distância Levenshtein entre duas palavras é o número mínimo de edições de um único caractere (ou seja, inserções, exclusões ou substituições) necessárias para mudar uma palavra para outra. Cada uma dessas operações tem um custo unitário.

 
Por exemplo, a distância Levenshtein entre kitten e sitting é 3. O script de edição mínimo que transforma o primeiro no último é:

kitten —> sitten (substitution of s for k)
sitten —> sittin (substitution of i for e)
sittin —> sitting (insertion of g at the end)

Pratique este problema

O problema Editar distância tem subestrutura ideal. Isso significa que o problema pode ser dividido em “subproblemas” menores e simples, que podem ser divididos em subproblemas ainda mais simples, e assim por diante, até que, finalmente, a solução se torne trivial.

 
Problema: Transformar string X[1…m] em Y[1…n] executando operações de edição na string X.

Subproblema: Transformar substring X[1…i] em Y[1…j] executando operações de edição na substring X.

 
Caso 1: Chegamos ao final de qualquer substring.

Se substring X está vazio, insira todos os caracteres restantes da substring Y em X. O custo desta operação é igual ao número de caracteres restantes na substring Y.

('', 'ABC') ——> ('ABC', 'ABC') (cost = 3)

Se substring Y está vazio, insira todos os caracteres restantes da substring X em Y. O custo desta operação é igual ao número de caracteres restantes na substring X.

('ABC', '') ——> ('', '') (cost = 3)

 
Caso 2: Os últimos caracteres da substring X e Y são os mesmos.

Se os últimos caracteres da substring X e substring Y correspondências, nada precisa ser feito - basta recorrer para a substring restante X[0…i-1], Y[0…j-1]. Como nenhuma operação de edição está envolvida, o custo será 0.

('ACC', 'ABC') ——> ('AC', 'AB') (cost = 0)

 
Caso 3: Os últimos caracteres da substring X e Y são diferentes.

Se os últimos caracteres da substring X e Y forem diferentes, retorne o mínimo das seguintes operações:

  • Insira o último caractere de Y em X. O tamanho de Y reduz em 1, e X continua o mesmo. Isso dá conta X[1…i], Y[1…j-1] à medida que avançamos na substring de destino, mas não na substring de origem.

     
    ('ABA', 'ABC') ——> ('ABAC', 'ABC') == ('ABA', 'AB') (using case 2)

  • Apague o último caractere de X. O tamanho de X reduz em 1, e Y continua o mesmo. Isso dá conta X[1…i-1], Y[1…j] à medida que avançamos na string de origem, mas não na string de destino.

     
    ('ABA', 'ABC') ——> ('AB', 'ABC')

  • Substituir (Substituir) o caractere atual de X pelo personagem atual Y. O tamanho de ambos X e Y reduz em 1. Isso representa X[1…i-1], Y[1…j-1] à medida que nos movemos na string de origem e de destino.

     
    ('ABA', 'ABC') —> ('ABC', 'ABC') == ('AB', 'AB') (using case 2)

    É basicamente o mesmo que o caso 2, onde os dois últimos caracteres correspondem, e movemos tanto a string de origem quanto a de destino, exceto que custa uma operação de edição.

Assim, podemos definir o problema recursivamentemente como:

             | max(i, j)                            when min(i, j) = 0
 
dist[i][j] = | dist[i – 1][j – 1]                   when X[i-1] == Y[j-1]
 
             | 1 + minimum { dist[i – 1][j],        when X[i-1] != Y[j-1]
             |            dist[i][j – 1],
             |            dist[i – 1][j – 1] }

A seguir está a implementação C++, Java e Python da ideia:

C++


Download  Executar código

Resultado:

The Levenshtein distance is 3

Java


Download  Executar código

Resultado:

The Levenshtein distance is 3

Python


Download  Executar código

Resultado:

The Levenshtein distance is 3

A complexidade de tempo da solução acima é exponencial e ocupa espaço na ligue para Stack.

 
Como visto acima, o problema subestrutura ideal. A solução acima também apresenta subproblemas sobrepostos. Se desenharmos a árvore de recursão da solução, podemos ver que os mesmos subproblemas são computados repetidamente. Sabemos que problemas com subestrutura ótima e subproblemas sobrepostos podem ser resolvidos usando programação dinâmica, na qual as soluções de subproblemas são memorizadas em vez de computadas repetidamente.

o memorandoA versão ized segue a abordagem de cima para baixo, pois primeiro dividimos o problema em subproblemas e, em seguida, calculamos e armazenamos valores. Também podemos resolver este problema de forma ascendente. Na abordagem de baixo para cima, resolvemos primeiro os subproblemas menores, depois resolvemos os subproblemas maiores a partir deles.

 
A invariante mantida ao longo do algoritmo é que podemos transformar o segmento inicial X[1…i] em Y[1…j] usando um mínimo de T[i, j] operações. No final, o elemento do array no canto inferior direito contém a resposta.

Por exemplo, deixe X ser 'gatinho', e Y estar 'sentado'. A distância de Levenshtein entre X e Y é 3. O i'th linha e j'th coluna na tabela abaixo mostra a distância Levenshtein da substring X[0…i-1] e Y[0…j-1].

Edit Distance Problem

A seguir está a implementação C++, Java e Python da ideia:

C++


Download  Executar código

Resultado:

The Levenshtein distance is 3

Java


Download  Executar código

Resultado:

The Levenshtein distance is 3

Python


Download  Executar código

Resultado:

The Levenshtein distance is 3

A complexidade de tempo da solução acima é O(m.n) e requer O(m.n) espaço extra, onde m é o comprimento da primeira string e n é o comprimento da segunda string. Acontece que apenas duas linhas da tabela são necessárias para a construção se não se quiser reconstruir as strings de entrada editadas (a linha anterior e a linha atual sendo calculadas).

 
Exercício: Modifique a versão iterativa para usar apenas duas linhas da matriz.

 
Referências: Distância Levenshtein – Wikipedia

Avalie esta postagem

Classificação média 4.85/5. Contagem de votos: 198

Sem votos até agora! Seja o primeiro a avaliar este post.

Lamentamos que este post não tenha sido útil para você!

Diga-nos como podemos melhorar este post?




Obrigado por ler.

Por favor, use nosso compilador online para postar código em comentários usando C, C++, Java, Python, JavaScript, C#, PHP e muitas outras linguagens de programação populares.

Como nós? Indique-nos aos seus amigos e ajude-nos a crescer. Codificação feliz :)



Se inscrever
Notificar de
guest
11 Comentários
Mais votados
O mais novo Mais antigo
Comentários em linha
Ver todos os comentários
NÃO siga este link ou você será banido do site!