Come è stato sottolineato, questo problema è simile al problema della distanza di modifica più comunemente noto (alla base della distanza di Levenshtein ). Ha anche elementi comuni, ad esempio con la distanza di distorsione temporale dinamica (la duplicazione o "balbuzie" nell'ultimo requisito).
Passi verso la programmazione dinamica
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.
Qui, l'ultima opzione dice sostanzialmente che convertire FOOX in BARX equivale a convertire FOO in BAR. Ciò significa che è possibile utilizzare l'opzione "Aggiungi lettera alla fine" per ottenere l'effetto balbuzie (duplicazione) e la cancellazione in un determinato momento. Il problema è che permette automaticamente di aggiungere un arbitrario personaggio al centro della stringa così , qualcosa che probabilmente non si vuole. (Questo "ignorare gli ultimi elementi identici" è il modo standard per ottenere la cancellazione e la balbuzie in posizioni arbitrarie. Rende il divieto di inserimenti arbitrari, pur consentendo aggiunte a entrambe le estremità, un po 'complicato, sebbene ...)
Ho incluso questa suddivisione anche se non svolge completamente il lavoro, nel caso in cui qualcun altro possa "salvarlo", in qualche modo, e perché lo utilizzo nella mia soluzione euristica, di seguito.
(Certo, se potessi ottenere una ripartizione come questa che in realtà ha definito la tua distanza, avresti solo bisogno di aggiungere memoization e avresti una soluzione. Tuttavia, poiché non stai solo lavorando con i prefissi, non t pensi di poter usare solo indici per la tua memoizzazione; potresti dover memorizzare le stringhe effettive e modificate per ogni chiamata, il che sarebbe enorme se le tue stringhe fossero di dimensioni sostanziali.)
Passi verso una soluzione euristica
Un altro approccio, che potrebbe essere più facile da capire e che potrebbe usare un po 'meno spazio, è quello di cercare il "percorso di modifica" più breve dalla prima stringa alla seconda, usando l' algoritmo (in sostanza, il migliore- prima diramazione). Lo spazio di ricerca sarebbe definito direttamente dalle tue operazioni di modifica. Ora, per una stringa di grandi dimensioni, lo farestiA∗A ∗ottenere un grande vicinato, in quanto potresti eliminare qualsiasi personaggio (dandoti un vicino per ogni potenziale eliminazione) o duplicare qualsiasi personaggio (di nuovo, dandoti un numero lineare di vicini), oltre ad aggiungere qualsiasi carattere alle due estremità, che darti un numero di vicini pari al doppio della dimensione dell'alfabeto. (Spero solo che non stai usando Unicode completo ;-) Con un fanout così grande, potresti ottenere una velocità piuttosto sostanziale usando un bidirezionale , o qualche parenteA∗ .
Per far funzionare , avresti bisogno di un limite inferiore per la distanza rimanente al tuo obiettivo. Non sono sicuro che ci sia una scelta ovvia qui, ma quello che potresti fare è implementare una soluzione di programmazione dinamica basata sulla decomposizione ricorsiva che ho dato sopra (di nuovo con possibili problemi di spazio se le tue stringhe sono molto lunghe). Mentre quella decomposizione non calcola esattamente la tua distanza, è garantito che è un limite inferiore (perché è più permissivo), il che significa che funzionerà come euristico in . (Quanto sarà stretto, non lo so, ma sarebbe corretto.) Naturalmente, la memoizzazione della tua funzione associata potrebbe essere condivisa tra tutti i calcoli del limite durante il tuoA ∗ A ∗A∗A∗A∗correre. (Un compromesso tempo / spazio lì.)
Così…
L'efficienza della mia soluzione proposta sembrerebbe dipendere un po 'da (1) la lunghezza delle stringhe e (2) dalla dimensione del tuo alfabeto. Se nessuno dei due è enorme, potrebbe funzionare. Questo è:
- Implementa il limite inferiore alla tua distanza usando la mia decomposizione ricorsiva e la programmazione dinamica (ad esempio, usando una funzione ricorsiva memorizzata).
- Implementa (o bidirezionale ) con le tue operazioni di modifica come "mosse" nello spazio degli stati e il limite inferiore basato sulla programmazione dinamica.A ∗A∗A∗
Non posso davvero dare alcuna garanzia su quanto sarebbe efficiente, ma dovrebbe essere corretto e probabilmente sarebbe molto meglio di una soluzione a forza bruta.
Se non altro, spero che questo ti dia alcune idee per ulteriori indagini.