Je suis tombé sur cette variante de edit-distance problem:
Concevez un algorithme qui transforme un mot source en un mot cible. par exemple: de tête en queue, à chaque étape, vous pouvez simplement remplacer un caractère et le mot doit être valide. Vous recevrez un dictionnaire.
C'est clairement une variation de edit distance problem, mais en édition distance, je ne me soucie pas de savoir si le mot est valide ou non. Alors, comment puis-je ajouter cette exigence pour modifier la distance.
Ceci peut être modélisé comme un problème de graphe. Vous pouvez considérer les mots comme des nœuds du graphe et deux nœuds sont connectés si et seulement s'ils ont la même longueur et diffèrent par un caractère.
Vous pouvez prétraiter le dictionnaire et créer ce graphique, qui devrait ressembler à ceci:
stack jack
| |
| |
smack back -- pack -- pick
Vous pouvez ensuite avoir un mappage du mot au nœud représentant le mot, pour cela vous pouvez utiliser une table de hachage, hauteur équilibrée BST ...
Une fois que le mappage ci-dessus est en place, tout ce que vous avez à faire est de voir s’il existe un chemin entre les deux nœuds du graphe, ce qui peut être facilement fait avec BFS ou DFS.
Vous pouvez donc résumer l’algorithme comme suit:
preprocess the dictionary and create the graph.
Given the two inputs words w1 and w2
if length(w1) != length(w2)
Not possible to convert
else
n1 = get_node(w1)
n2 = get_node(w2)
if(path_exists(n1,n2))
Possible and nodes in the path represent intermediary words
else
Not possible
l'approche de graphe de codaddict est valide, bien qu'il faille O (n ^ 2) pour construire chaque graphe, n étant le nombre de mots d'une longueur donnée. Si cela pose un problème, vous pouvez construire un bk-tree beaucoup plus efficacement, ce qui permet de trouver tous les mots avec une distance d'édition (dans ce cas, 1) d'un mot cible.
Créez un graphique avec chaque nœud représentant Word dans le dictionnaire. Ajoutez un bord entre deux nœuds Word, si leurs mots correspondants se trouvent à une distance de modification de 1. Le nombre minimal de transformations requis correspond à la longueur du chemin le plus court entre le nœud source et le nœud de destination.
Je ne pense pas que ce soit la distance d'édition.
Je pense que cela peut être fait en utilisant un graphique. Il vous suffit de construire un graphique à partir de votre dictionnaire et d'essayer de naviguer à l'aide de votre algorithme de traversée de graphique préféré.
Vous pouvez simplement utiliser un suivi arrière récursif, mais c'est loin d'être la solution la plus optimale.
# Given two words of equal length that are in a dictionary, write a method to transform one Word into another Word by changing only
# one letter at a time. The new Word you get in each step must be in the
# dictionary.
# def transform(english_words, start, end):
# transform(english_words, 'damp', 'like')
# ['damp', 'lamp', 'limp', 'Lime', 'like']
# ['damp', 'camp', 'came', 'lame', 'Lime', 'like']
def is_diff_one(str1, str2):
if len(str1) != len(str2):
return False
count = 0
for i in range(0, len(str1)):
if str1[i] != str2[i]:
count = count + 1
if count == 1:
return True
return False
potential_ans = []
def transform(english_words, start, end, count):
global potential_ans
if count == 0:
count = count + 1
potential_ans = [start]
if start == end:
print potential_ans
return potential_ans
for w in english_words:
if is_diff_one(w, start) and w not in potential_ans:
potential_ans.append(w)
transform(english_words, w, end, count)
potential_ans[:-1]
return None
english_words = set(['damp', 'camp', 'came', 'lame', 'Lime', 'like'])
transform(english_words, 'damp', 'lame', 0)
Je ne pense pas que nous ayons besoin d'un graphique ou d'une autre structure de données compliquée. Mon idée est de charger le dictionnaire en tant que HashSet
et d'utiliser la méthode contains()
pour savoir si le mot existe dans le dictionnaire ou non.
Veuillez vérifier ce pseudocode pour voir mon idée:
Two words are given: START and STOP.
//List is our "way" from words START to STOP, so, we add the original Word to it first.
list.add(START);
//Finish to change the Word when START equals STOP.
while(!START.equals(STOP))
//Change each letter at START to the letter to STOP one by one and check if such Word exists.
for (int i = 0, i<STOP.length, i++){
char temp = START[i];
START[i] = STOP[i];
//If the Word exists add a new Word to the list of results.
//And change another letter in the new Word with the next pass of the loop.
if dictionary.contains(START)
list.add(START)
//If the Word doesn't exist, leave it like it was and try to change another letter with the next pass of the loop.
else START[i] = temp;}
return list;
Si je comprends bien, mon code devrait fonctionner comme ça:
Entrée : DAMP, LIKE
Sortie : DAMP, LAMP, LIMP, Lime, LIKE
Entrée : BACK, PICK
Sortie : RETOUR, PACK, PICK
C'est le code C # pour résoudre le problème avec BFS:
//use a hash set for a fast check if a Word is already in the dictionary
static HashSet<string> Dictionary = new HashSet<string>();
//dictionary used to find the parent in every node in the graph and to avoid traversing an already traversed node
static Dictionary<string, string> parents = new Dictionary<string, string>();
public static List<string> FindPath(List<string> input, string start, string end)
{
char[] allcharacters = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
foreach (string s in input)
Dictionary.Add(s);
List<string> currentFrontier = new List<string>();
List<string> nextFrontier = new List<string>();
currentFrontier.Add(start);
while (currentFrontier.Count > 0)
{
foreach (string s in currentFrontier)
{
for (int i = 0; i < s.Length; i++)
{
foreach (char c in allcharacters)
{
StringBuilder newWordBuilder = new StringBuilder(s);
newWordBuilder[i] = c;
string newWord = newWordBuilder.ToString();
if (Dictionary.Contains(newWord))
{
//avoid traversing a previously traversed node
if (!parents.Keys.Contains(newWord))
{
parents.Add(newWord.ToString(), s);
nextFrontier.Add(newWord);
}
}
if (newWord.ToString() == end)
{
return ExtractPath(start, end);
}
}
}
}
currentFrontier.Clear();
currentFrontier.Concat(nextFrontier);
nextFrontier.Clear();
}
throw new ArgumentException("The given dictionary cannot be used to get a path from start to end");
}
private static List<string> ExtractPath(string start,string end)
{
List<string> path = new List<string>();
string current = end;
path.Add(end);
while (current != start)
{
current = parents[current];
path.Add(current);
}
path.Reverse();
return path;
}