web-dev-qa-db-fra.com

Algorithme pour transformer un mot en un autre avec des mots valides

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.

38
gameover

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
42
codaddict

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.

10
Nick Johnson

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.

3
prasadvk

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é.

2
Yuliy

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)
1
Pradeep Vairamani

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 

0
Boris

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;
    }
0
Muhammad Adel