📜  字梯 – 设置 2(双向 BFS)(1)

📅  最后修改于: 2023-12-03 14:53:25.808000             🧑  作者: Mango

字梯 – 设置 2(双向 BFS)

简介

“字梯”是指通过变换一个单词的一个字母来得到另一个单词,在每一步变换过程中所得到的单词都必须是合法且存在于给定字典中。

这个算法主要解决的问题是给定两个单词和一个字典,找到从起始单词到结束单词需要变换多少步。这个问题也被称作“单词距离问题”。

单向 BFS(Breadth First Search,广度优先搜索)算法是一种常见的解决字梯问题的方法,但是随着字典规模的增大,其耗时也会增加。为了解决这个问题,可以采用双向 BFS 算法来加速搜索过程。

双向 BFS 算法的思路是同时从起始单词和结束单词开始搜索,直到两个搜索路径上出现了相同的单词,就可以得到最短的距离。双向 BFS 算法的时间复杂度是 O(b^(d/2)),其中 b 是字典中一个单词有多少个相邻单词,d 是起始单词和结束单词之间的距离。

步骤

1.定义起始单词和结束单词,创建两个队列分别用于存储从起始单词开始搜索的路径和从结束单词开始搜索的路径。

2.创建两个集合分别用于存储已经访问过的单词。为了简化计算过程,可以让起始单词和结束单词都在已访问集合中。

3.从起始单词的队列和结束单词的队列中各取出一个单词,分别进行以下操作:

  • 遍历当前单词的所有相邻单词(即只差一个字母的单词),如果相邻单词存在于已访问集合中,则忽略该单词。如果相邻单词存在于另一个搜索路径中,则说明已经找到了对应单词,此时可以返回两个路径的长度和。

  • 否则,将相邻单词加入队列和已访问集合,同时记录该单词的前缀(即从起始单词或结束单词开始访问到该单词的路径)。

4.如果两个队列中都有单词,则重复步骤 3。

5.如果搜索过程中没有找到相同的单词,则说明起始单词和结束单词不连通。

代码示例
def ladder_length(begin_word: str, end_word: str, word_list: List[str]) -> int:
    if end_word not in word_list:
        return 0
    
    # 定义两个集合和两个队列
    begin_set, end_set = {begin_word}, {end_word}
    begin_visited, end_visited = {begin_word}, {end_word}
    word_list = set(word_list)
    
    step = 1
    
    # 双向 BFS 开始
    while begin_set:
        if len(begin_set) > len(end_set):
            begin_set, end_set = end_set, begin_set # 优化:始终从数量较小的集合开始遍历
        
        new_set = set()
        for word in begin_set:
            for i in range(len(word)):
                for ch in 'abcdefghijklmnopqrstuvwxyz':
                    if ch != word[i]:
                        new_word = word[:i] + ch + word[i+1:]
                        
                        if new_word in end_set:
                            return step + 1
                        
                        if new_word in word_list and new_word not in begin_visited:
                            new_set.add(new_word)
                            begin_visited.add(new_word)
        
        begin_set = new_set
        step += 1
        
        if len(begin_set) > len(end_set):
            begin_set, end_set = end_set, begin_set
        
        new_set = set()
        for word in end_set:
            for i in range(len(word)):
                for ch in 'abcdefghijklmnopqrstuvwxyz':
                    if ch != word[i]:
                        new_word = word[:i] + ch + word[i+1:]
                        
                        if new_word in begin_set:
                            return step + 1
                            
                        if new_word in word_list and new_word not in end_visited:
                            new_set.add(new_word)
                            end_visited.add(new_word)
        
        end_set = new_set
        step += 1
        
    return 0 # 没有找到连通路径