5

I've already put a lot of work into this problem and am really near the tail end. The overall goal was to create min length word ladders between two five letter words where each 'rung' of the ladder is one letter different from the previous word. For example:

[heads, heals, hells, halls, hails, tails]

The program starts where you must input a beginning and end word and the length of the ladder you want, and the program must solve it. I've gotten pretty far already, so I'll spare much of the details to explain my current situation.

Say I'm going from "babes" to "child" and I'm looking for a 10 letter word ladder.

I have many thousand pairs of words, where the two pairs are one letter different from eachother. Here is just a small sample of some of the pairs.

[(bares, babes), (banes, babes), (bates, babes), (babel, babes), (bases, babes), (bales, babes)...] etc.

This goes on for a long time, but it is guaranteed in there that my destination word exists, and that there is a path between my starting word (babes) and my ending word (child) is in there, and that ladder is 10 words long.

How do I accomplish this?

EDIT: I have already implemented a graph, and am using BFS to go from the starting to ending word, which works.

public List<T> minLengthPath(T src, T dest, int length) 
{
    T start = src;

    Deque<T> queue = new LinkedList<T>();                       //Holds items to visit
    Queue<List<T>> ladder = new LinkedList<List<T>>();      //Holds all the ladders?
    Set<T> checker = new HashSet<T>();                          //Holds visited items

    queue.add(start);
    checker.add(start);

    while(!queue.isEmpty()){
        T slot = queue.remove();
        if(slot.equals(dest)) 
        { 
            System.out.println(slot);
            return null;  //Should be returning ladder
        }
        Set<Pair<Integer, T>> thing = this.edges.get(slot);
        Set<T> edges = findEdges(thing);     //Returns the edges of the node

        Iterator<T> check = edges.iterator();
        for(int a = 0; a < edges.size(); a ++) 
        {
            T hole = check.next();
            if(!checker.contains(hole))
            {
                checker.add(hole);
                queue.add(hole);
            }
        }           
    }
    return null;
}
Donnie
  • 45,732
  • 10
  • 64
  • 86
Hoser
  • 4,974
  • 9
  • 45
  • 66
  • I haven't tried working with this. I had a completely different approach a while ago but it wasn't working so I scrapped it in favor of this. I've been trying to think of some algorithms, but can't come up with anything promising. I was thinking maybe a sort of depth first search approach would work, but I'm not sure how to accomplish that just yet. – Hoser Apr 14 '12 at 22:42

1 Answers1

10

Well, You are describing a graph problem which is known as the shortest path problem.

In here, your graph is G = (V,E), where V = { all words} and E = {(u,v) | there is a direct "ladder" from word u to word v}.

In this case, the graph is unweighted, so you can use a BFS to find the shortest path from the source to the target.

One can also use A* algorithm for it, after finding an admissible heuristic function that evaluates how far are you from the target node. As noted by @trutheality, one possible heuristic function is the number of mismatched letters.

Note that you don't actually need to "create" the whole graph before you start the search, you can generate it "on the fly" using a function: next(w) = { u | (w,u) is in E, or in other words - there is a direct ladder from w to u }

After finding what is the length of the shortest path by running a BFS, you can also find what exactly the path was - by going back. This thread explains this issue with more details. The idea is maintaining a Map - where the key is the vertex, and the value is how this vertex was discovered - the vertex that led to discovering the key. After the BFS is done, you only need to go from the target back to the source, and you got your path! [reversed, of course...]

Community
  • 1
  • 1
amit
  • 175,853
  • 27
  • 231
  • 333
  • I've actually already implemented a graph. Each node is a word, and the edges between each node are the words that are one different from said node. I'm using BFS to find the end node, which works, I can find the end word starting at the beginning word, but I don't know how to build up the word ladder as I go from my starting to ending word. – Hoser Apr 14 '12 at 22:46
  • @user1333854: look at my last sentence [just added it] it describes how to find the actual path found by BFS. The linked thread explains it with more details. Is that what you mean? – amit Apr 14 '12 at 22:47
  • @trutheality As we're talking about BFS, a stack would be more appropriate, but basically it's what you're saying. – biziclop Apr 14 '12 at 22:48
  • A stack would work better, but I am required to use Queues here. I'll look over your added info. – Hoser Apr 14 '12 at 22:49
  • @trutheality: Thanks! I added your suggestion to the answer - with the right credit, of course! :) – amit Apr 14 '12 at 22:52
  • Okay, I read over that thread... I don't have too much experience using maps but I figure it can't be too hard. So what you're suggesting is that each time I step into a new node, I should 'map' the two steps I took using a map data structure? – Hoser Apr 14 '12 at 22:55
  • @user1333854: Exactly, and when you are done, follow your way on reverse, from the target to the source. You might want to use the interface [Map](http://docs.oracle.com/javase/6/docs/api/java/util/Map.html) for it, and the class [HashMap](http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html) – amit Apr 14 '12 at 22:58
  • When I 'map' something, say going from the word child to chill, I should map it as put(child,chill)? – Hoser Apr 14 '12 at 23:01
  • @user1333854: No, you should do it the other way around, `put(chill,child)`, since chill was discovered via the vertex child. Note that it makes sense, since each vertex is discovered only once - so it is a unique key. Later you will be able to trace the way back to the source by looking for it. – amit Apr 14 '12 at 23:03
  • Okay. I see I'm supposed to keep comment discussion to a minimum so I'll try to keep this brief, but how after I map every jump I made do I so easily follow it back? To me the only way I would know how to do that is another search of sorts where I just try all possible combinations, but my program is actually supposed to return the specific word ladder that was taken. How is this accomplished? That is what still puzzles me. – Hoser Apr 14 '12 at 23:07
  • @user1333854 `map.get(target)` gets you how you discovered `target`. `v = map.get(v)` assigns to `v` how you discovered `v`. by iteratively adding `v` to a list, and doing `v = map.get(v)` until you get that `v` is the source, you get the actual path used by the BFS, which is the shortest. Note that it requires exactly `d` iterations, where `d` is the length of the shortest path. – amit Apr 14 '12 at 23:10
  • Okay I'll go ahead and implement this. I'll try to keep further questions to a minimum. Thank you for your help! – Hoser Apr 14 '12 at 23:15