Task: Word ladder


C++: #include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>

using word_map = std::map<size_t, std::vector<std::string>>;


bool one_away(const std::string& s1, const std::string& s2) {
    if (s1.size() != s2.size())
        return false;
    bool result = false;
    for (size_t i = 0, n = s1.size(); i != n; ++i) {
        if (s1[i] != s2[i]) {
            if (result)
                return false;
            result = true;
        }
    }
    return result;
}


template <typename iterator_type, typename separator_type>
std::string join(iterator_type begin, iterator_type end,
                 separator_type separator) {
    std::string result;
    if (begin != end) {
        result += *begin++;
        for (; begin != end; ++begin) {
            result += separator;
            result += *begin;
        }
    }
    return result;
}




bool word_ladder(const word_map& words, const std::string& from,
                 const std::string& to) {
    auto w = words.find(from.size());
    if (w != words.end()) {
        auto poss = w->second;
        std::vector<std::vector<std::string>> queue{{from}};
        while (!queue.empty()) {
            auto curr = queue.front();
            queue.erase(queue.begin());
            for (auto i = poss.begin(); i != poss.end();) {
                if (!one_away(*i, curr.back())) {
                    ++i;
                    continue;
                }
                if (to == *i) {
                    curr.push_back(to);
                    std::cout << join(curr.begin(), curr.end(), " -> ") << '\n';
                    return true;
                }
                std::vector<std::string> temp(curr);
                temp.push_back(*i);
                queue.push_back(std::move(temp));
                i = poss.erase(i);
            }
        }
    }
    std::cout << from << " into " << to << " cannot be done.\n";
    return false;
}

int main() {
    word_map words;
    std::ifstream in("unixdict.txt");
    if (!in) {
        std::cerr << "Cannot open file unixdict.txt.\n";
        return EXIT_FAILURE;
    }
    std::string word;
    while (getline(in, word))
        words[word.size()].push_back(word);
    word_ladder(words, "boy", "man");
    word_ladder(words, "girl", "lady");
    word_ladder(words, "john", "jane");
    word_ladder(words, "child", "adult");
    word_ladder(words, "cat", "dog");
    word_ladder(words, "lead", "gold");
    word_ladder(words, "white", "black");
    word_ladder(words, "bubble", "tickle");
    return EXIT_SUCCESS;
}




Java: import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.stream.IntStream;

public class WordLadder {
    private static int distance(String s1, String s2) {
        assert s1.length() == s2.length();
        return (int) IntStream.range(0, s1.length())
            .filter(i -> s1.charAt(i) != s2.charAt(i))
            .count();
    }

    private static void wordLadder(Map<Integer, Set<String>> words, String fw, String tw) {
        wordLadder(words, fw, tw, 8);
    }

    private static void wordLadder(Map<Integer, Set<String>> words, String fw, String tw, int limit) {
        if (fw.length() != tw.length()) {
            throw new IllegalArgumentException("From word and to word must have the same length");
        }

        Set<String> ws = words.get(fw.length());
        if (ws.contains(fw)) {
            List<String> primeList = new ArrayList<>();
            primeList.add(fw);

            PriorityQueue<List<String>> queue = new PriorityQueue<>((chain1, chain2) -> {
                int cmp1 = Integer.compare(chain1.size(), chain2.size());
                if (cmp1 == 0) {
                    String last1 = chain1.get(chain1.size() - 1);
                    int d1 = distance(last1, tw);

                    String last2 = chain2.get(chain2.size() - 1);
                    int d2 = distance(last2, tw);

                    return Integer.compare(d1, d2);
                }
                return cmp1;
            });
            queue.add(primeList);

            while (queue.size() > 0) {
                List<String> curr = queue.remove();
                if (curr.size() > limit) {
                    continue;
                }

                String last = curr.get(curr.size() - 1);
                for (String word : ws) {
                    if (distance(last, word) == 1) {
                        if (word.equals(tw)) {
                            curr.add(word);
                            System.out.println(String.join(" -> ", curr));
                            return;
                        }

                        if (!curr.contains(word)) {
                            List<String> cp = new ArrayList<>(curr);
                            cp.add(word);
                            queue.add(cp);
                        }
                    }
                }
            }
        }

        System.err.printf("Cannot turn `%s` into `%s`%n", fw, tw);
    }

    public static void main(String[] args) throws IOException {
        Map<Integer, Set<String>> words = new HashMap<>();
        for (String line : Files.readAllLines(Path.of("unixdict.txt"))) {
            Set<String> wl = words.computeIfAbsent(line.length(), HashSet::new);
            wl.add(line);
        }

        wordLadder(words, "boy", "man");
        wordLadder(words, "girl", "lady");
        wordLadder(words, "john", "jane");
        wordLadder(words, "child", "adult");
        wordLadder(words, "cat", "dog");
        wordLadder(words, "lead", "gold");
        wordLadder(words, "white", "black");
        wordLadder(words, "bubble", "tickle", 12);
    }
}