Um ponto de partida aproximado da pesquisa de cadeias é o da distância de Levenshtein . Esse algoritmo conta o número de edições de um único caractere (inserção, exclusão e substituição) para alterar uma palavra em outra.
Um exemplo disso é kitten
-> sitting
que tem uma distância de edição de três
- k itten -> s itten (substitua 's' por 'k')
- sitt e n -> sitt i n (substitua 'i' por 'e')
- sittin -> sittin g (adicione 'g' no final)
Existem variações nesse algoritmo, notadamente a distância Damerau – Levenshtein, que permite a transposição de dois caracteres adjacentes ('hte' para 'the' tem uma distância DL de 1 e uma distância Levenshtein de 2) e, portanto, muitas vezes é mais apropriado para verificação ortográfica. Existem outras variações para aplicações em que as lacunas são importantes (cadeias de DNA).
A distância de Levenshtein é bem conhecida e não é muito difícil de encontrar (uma vez tive motivos para buscar uma implementação dela como uma função no oracle - era muito mais rápido do que baixar todos os dados e depois executar o lado do código de consulta). O Rosettacode possui uma infinidade (54) de implementações da distância Levenshtein (observe que algumas linguagens têm isso como parte da biblioteca de cadeias em algum lugar - se você estiver executando Java, observe o apache commons lang ). O Wikilivros possui 31 implementações e uma rápida olhada nos dois não mostra o mesmo código para o mesmo idioma.
A maneira como isso funciona é construir uma matriz que corresponde ao relacionamento entre as duas strings:
.kitten
.0123456
s1123456
i2212345
t3321234
t4432123
i5543223
n6654332
g7765443
A .
linha e a coluna representam que você pode chegar à string de destino inserindo 'apenas' cada letra de uma string vazia. Este não é o caso ideal, mas existe para propagar o algoritmo.
Se o valor for o mesmo desse ponto ('i' == 'i'), o valor será o mesmo que o valor na diagonal à esquerda. Se os dois pontos forem diferentes ('s'! = 'K'), o valor será o mínimo de:
- diagonal para cima e para a esquerda + 1 (uma substituição)
- diretamente acima de + 1 (uma inserção)
- diretamente à esquerda + 1 (uma exclusão)
O valor de retorno da distância de edição é o valor no canto inferior direito da matriz.
Se você seguir do canto inferior direito para o canto superior esquerdo com o mínimo, poderá ver as edições feitas:
.kitten
.0. .
s.1 .
i 1 .
t 1 .
t 1.
i.....2
n 2
g......3
Observe que essa é a abordagem que consome muita memória. Ele pode ser reduzido no escopo da memória, não criando a matriz completa - tudo o que o algoritmo se importa é um subconjunto dos dados e pode ser reduzido de um N*M
espaço para o outro 2*max(N,M)
apenas armazenando a linha anterior (e o que foi calculado na corrente linha). Projeto de código mostra como isso pode ser feito (com código C # para download).