Como foi apontado, esse problema é semelhante ao problema de distância de edição mais conhecido (subjacente à distância de Levenshtein ). Ele também possui pontos em comum com, por exemplo, a distância dinâmica de distorção do tempo (a duplicação ou "gagueira") em seu último requisito.
Passos para a programação dinâmica
Minha primeira tentativa de decomposição recursiva ao longo das linhas de distância de Levenshtein e distância dinâmica de distorção do tempo foi algo como o seguinte (para e ), com sendo definido como
x=x1…xny=y1…ymd(x,y)
min⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪d(x,y1…ym−1)+1d(x,y2…ym)+1d(x,y1…ym/2)+1d(x1…xn/2,y)+1d(x1…xn,y)+1d(x1…xn−1,y1…ym−1)if y=y1…ym/2y1…ym/2if x=x1…xn/2x1…xn/2if yn=ym▻ Add letter at end▻ Add letter at beginning▻ Doubling▻ Halving▻ Deletion▻ Ignoring last elt.
Aqui, a última opção basicamente diz que converter FOOX em BARX é equivalente a converter FOOX em BAR. Isso significa que você pode usar a opção "adicionar letra no final" para obter o efeito de gagueira (duplicação) e a exclusão em algum momento. O problema é que ele automaticamente permite que você adicione uma arbitrária personagem no meio da corda bem , algo que você provavelmente não quer. (Esse "ignorar os últimos elementos idênticos" é a maneira padrão de obter exclusão e gagueira em posições arbitrárias. Torna proibir inserções arbitrárias e, ao mesmo tempo, permitir adições em ambas as extremidades, um pouco complicado ...)
Eu incluí esse detalhamento, mesmo que ele não funcione completamente, caso alguém possa "resgatá-lo" de alguma forma - e porque eu o uso na minha solução heurística abaixo.
(Obviamente, se você pudesse obter uma análise como essa que realmente definisse sua distância, seria necessário apenas adicionar memorização e ter uma solução. No entanto, como você não está apenas trabalhando com prefixos, eu não acho que você poderia usar apenas índices para sua memorização; talvez seja necessário armazenar as strings modificadas reais para cada chamada, o que seria enorme se as strings fossem de tamanho substancial.)
Passos em direção a uma solução heurística
Outra abordagem, que pode ser mais fácil de entender e que pode usar um pouco menos de espaço, é procurar o menor “caminho de edição” da sua primeira string para a sua segunda, usando o algoritmo (basicamente, o melhor primeiro ramo e limite). O espaço de pesquisa seria definido diretamente pelas suas operações de edição. Agora, para uma grande cadeia, você fariaA∗A ∗obtenha uma vizinhança grande, pois você pode excluir qualquer caractere (fornecendo um vizinho para cada exclusão em potencial) ou duplicar qualquer caractere (novamente, fornecendo um número linear de vizinhos), além de adicionar qualquer caractere em cada extremidade, o que dê a você um número de vizinhos igual ao dobro do tamanho do alfabeto. (Só espero que você não esteja usando Unicode completo ;-) Com um fanout tão grande, é possível obter uma aceleração substancial usando um bidirecional ou algum parenteA∗ .
Para fazer o funcionar, você precisará de um limite inferior para a distância restante até o seu alvo. Não tenho certeza se existe uma escolha óbvia aqui, mas o que você poderia fazer é implementar uma solução de programação dinâmica com base na decomposição recursiva que forneci acima (novamente com possíveis problemas de espaço, se suas seqüências de caracteres forem muito longas). Embora essa decomposição não calcule exatamente a sua distância, ela é garantida como um limite inferior (porque é mais permissivo), o que significa que funcionará como uma heurística em . (Não sei quão apertado será, mas estaria correto.) É claro que a memorização de sua função vinculada poderia ser compartilhada em todos os cálculos do limite durante seuA ∗ A ∗A∗A∗A∗corre. (Uma troca de tempo / espaço lá.)
Então…
A eficiência da minha solução proposta parece representar bastante (1) o comprimento das suas strings e (2) o tamanho do seu alfabeto. Se nenhum dos dois for enorme, pode funcionar. Isso é:
- Implemente o limite inferior à sua distância usando minha decomposição recursiva e programação dinâmica (por exemplo, usando uma função recursiva memorizada).
- Implemente (ou bidirecional ) com suas operações de edição como "movimentos" no espaço de estados e o limite inferior baseado em programação dinâmica.A ∗A∗A∗
Eu realmente não posso dar nenhuma garantia de quão eficiente seria, mas deve estar correto, e provavelmente seria muito melhor do que uma solução de força bruta.
Se nada mais, espero que isso lhe dê algumas idéias para futuras investigações.