LeetCode - 127.单词接龙

285 阅读2分钟

1.题目简述

字典 wordList 中从单词 beginWord 和 endWord 的转换序列是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk

  1. 每一对相邻的单词只差一个字母
  2. 对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中
  3. sk == endWord

例子:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]

输出:5

解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5

详情请查看 LeetCode 官网:127. 单词接龙 - 力扣(LeetCode)

2.方法

由题可知,转换序列中某个单词与其前后单词的差别只有一个字符,因此可以通过广度搜索遍历(BFS)方法遍历其变化过程。

本题直接对单词的所有变化情况进行穷举,会造成超时(文末给出暴力方法代码)。因此需要用上小 Trick。

以单词 hit 为例,可知该单词的变化情况有 3 种:*ith*thi*。遍历 wordList 建立以下映射情况(根据例子中的情况):

  • hit \rightarrow *ith*thi*
  • *it \rightarrow hit
  • h*t \rightarrow hothit
  • hi* \rightarrow hit

在本方法中通过哈希表保存每个单词的id号;通过数组保存以上的映射情况,数组通过id号来指代单词。

以下是例子中的完整映射情况:

word2Id
hot -> 0 *ot -> 1 h*t -> 2 ho* -> 3
dot -> 4 d*t -> 5 do* -> 6
dog -> 7 d*g -> 9
lot -> 10 l*t -> 11 lo* -> 12
log -> 13 l*g -> 14
cog -> 15 c*g -> 16 co* -> 17
hit -> 18 *it -> 19 hi* -> 20
edges
单词 id 映射 id 单词 id 映射 id
0 1、2、3 1 0、4、10
2 0、18 3 0
4 1、5、6 5 4
6 4、7 7 8、9、6
8 7、13、15 9 7
10 1、11、12 11 10
12 10、13 13 8、14、12
14 13 15 8、16、17
16 15 17 15
18 19、2、20 19 18
20 18

代码:

Map<String,Integer> word2Id;
List<List<Integer>> edges;
int nodeNum = 0;

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    word2Id = new HashMap<>();
    edges = new ArrayList<>();
    nodeNum = 0;
    for(String word: wordList){
        addEdge(word);
    }
    addEdge(beginWord);
    if(!word2Id.containsKey(endWord)){
        return 0;
    }

    int[] dis = new int[nodeNum];
    Arrays.fill(dis,Integer.MAX_VALUE);
    int beginId = word2Id.get(beginWord),endId = word2Id.get(endWord);
    dis[beginId] = 0;
    Queue<Integer> queue = new LinkedList<>();
    queue.offer(beginId);
    while(!queue.isEmpty()){
        int curId = queue.poll();
        if(curId == endId){
            return dis[endId] + 1;
        }
        // hit -> *it、h*t、hi*
        for(int changeId: edges.get(curId)){
            // *it -> hit
            for(int nextId: edges.get(changeId)){
                if(dis[nextId] == Integer.MAX_VALUE){
                    dis[nextId] = dis[curId] + 1;
                    queue.offer(nextId);
                }
            }
        }
    }
    return 0;
}

void addEdge(String word){
    addWord(word);
    int id = word2Id.get(word);
    char[] array = word.toCharArray();
    for(int i=0;i<array.length;i++){
        char temp = array[i];
        array[i] = '*';
        String newWord = new String(array);
        addWord(newWord);
        int newId = word2Id.get(newWord);
        edges.get(id).add(newId);
        edges.get(newId).add(id);
        array[i] = temp;
    }
}

void addWord(String word){
    if(!word2Id.containsKey(word)){
        word2Id.put(word,nodeNum++);
        edges.add(new ArrayList<>());
    }
}

3.暴力穷举方法

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    Set<String> wordSet = new HashSet<>();
    Set<String> visited = new HashSet<>();
    for(String word: wordList){
        wordSet.add(word);
    }

    if(beginWord.equals(endWord)) return 1;
    if(!wordSet.contains(endWord)) return 0;
    Queue<String> queue = new LinkedList<>();
    queue.offer(beginWord);
    int step = 2;
    while(!queue.isEmpty()){
        int len = queue.size();
        for(int i=0;i<len;i++){
            StringBuilder sb = new StringBuilder(queue.poll());
            for(int j=0;j<sb.length();j++){
                char origin = sb.charAt(j);
                for(int k=0;k<26;k++){
                    sb.setCharAt(j,(char)('a'+k));
                    String next = sb.toString();
                    if(wordList.contains(next) && !visited.contains(next)){
                        if(endWord.equals(next)){
                            return step;
                        }
                        visited.add(next);
                        queue.offer(next);
                    }
                }
                sb.setCharAt(j,origin);
            }
        }
        step++;
    }
    return 0;
}