图论 10. 字符串接龙

图论 10. 字符串接龙

110. 字符串接龙

代码随想录

卡码网无难度标识

  • 思路:

    • 问题相当于:每个字符串都是一个结点,求beginStr到endStr的最短路径长度

    • 两长度相同的字符是否能相互转换,只需要比较它们是否满足只有一个同位置字符不相同

      isNeighbor(s1, s2)

    • 显然由于两字符如果能相互转化,那必然是双向关系,所以是无向图

      创建无向图时,要记得录入双向边

    • 给字符串编号,将字符串映射为连续的编号(从0到n+1)

      beginStr编号0

      编号1~n为中转字符串编号

      endStr编号n+1

    • 由于要求最短路径,所以使用邻接表构造无向图比较方便进行路径的扩展,一共n+2个字符串(结点)

      前面的结点编号就是邻接表的对应索引

    • 开始图遍历,从起点编号0(beginStr)开始,寻找其到终点编号n+1(endStr)的最短路径,并获取长度。

      最短路径显然要用bfs

    • 需要使用长度为n+2的访问表,控制无向图bfs拓展结点时不回头重复拓展!

      每次拓展,仅拓展未访问过的结点

      新结点拓展(入队)时,要立刻判断当前待拓展结点是否为目的地结点,如果是的话直接终止遍历即可

    • 使用全局变量count​存储从 beginStr 转换到 endStr需要的最短转换序列中的字符串数量(相当于最短路径长度)。若不存在这样的转换序列,则count​为0。

  • bfs代码:

    import sys
    from collections import deque
    
    def isNeighbor(s1, s2):
        # 输入s1,s2两个相同长度字符串
        # 比较输出它们是否满足只能改变一个字符就能相互转换
        diff_count = 0 # 记录不同字符的个数
        for i in range(len(s1)):
            if s1[i] != s2[i]: diff_count += 1
        if diff_count == 1: return True
        else: return False
    
    
    def bfs(graph, visited, s, e):
        # visited访问表,用于控制无向图bfs拓展结点时不回头重复拓展
        # s为当前遍历到的结点编号
        # e为目的地结点编号
        global count # 记录当前路径长度
    
        # 计入s结点
        count = 1 
        visited[s] = True
        q = deque([s])
    
        while q:
            count += 1 # 拓展下一层
            level_len = len(q) # 当前层长度
            for i in range(level_len): # 当前层
                cur_node = q.popleft() # 当前结点
                for node in graph[cur_node]: # 拓展下一层结点入队
                    # 仅拓展未访问过的结点
                    if not visited[node]:
                        if node == e: # 如果当前待拓展结点是目的地结点,直接终止遍历即可
                            return
                        visited[node] = True
                        q.append(node)
    
        count = 0 # 遍历结束都没找到路径,重置为0
    
    def main():
        # 每个字符串都是一个结点,求beginStr到endStr的最短路径长度
        # 是无向图
        lines = sys.stdin.readlines()
        n = int(lines[0].strip()) # 中转字符串列表长度
        beginStr, endStr = lines[1].strip().split()
    
        # 给字符串编号,并记录字符串与编号的映射
        strList = [] # 中转字符串列表
        # strToNum = {} # 字典,存储字符串->编号的映射,便于查找
        numToStr = {} # 字典,存储编号->字符串的映射,便于查找
        # strToNum[beginStr] = 0 # beginStr编号0
        numToStr[0] = beginStr
        for i in range(n):
            strList.append(lines[i+2].strip())
            # strToNum[strList[-1]] = i + 1 # 编号1~n为中转字符串编号
            numToStr[i+1] = strList[-1]
        # strToNum[endStr] = n + 1 # endStr编号n+1
        numToStr[n+1] = endStr
    
        # 构造无向图,使用邻接表,一共n+2个字符串(结点)
        graph = [[] for _ in range(n + 2)]
        for i in range(n + 2):
            for j in range(i): # 这样能保证只取上三角,不会重复
                if isNeighbor(numToStr[i], numToStr[j]):
                    graph[i].append(j)
                    graph[j].append(i) # 无向图,其实就相当于每个边都双向
    
        # 开始图遍历,从起点编号0(beginStr)开始,寻找其到终点编号n+1(endStr)的最短路径,并获取长度
        # 找最短路径要用bfs
        global count
        visited = [False] * (n + 2) # 访问表,结点编号为对应索引
        bfs(graph, visited, 0, n + 1)
    
        print(count)
    
    
    if __name__ == '__main__':
        count = 0 # 存储从 beginStr 转换到 endStr需要的最短转换序列中的字符串数量(相当于最短路径长度)。不存在这样的转换序列,则为0。
        main()
    
  • 代码随想录上的代码和我思路大致一样,但是写法更精简一些:

    注意这里bfs时会同时将字符串以及对应所处的长度step入队,并在popleft时直接popleft两个

    而我是用层序遍历的思想来控制层级的,区别不大

    另外,这里用for i in range(n): if visit[i]==False and judge(strlist[i],str):来直接寻找当前结点的下一个待拓展结点,省去了邻接表的构建,也可以学习一下

    def judge(s1,s2):
        count=0
        for i in range(len(s1)):
            if s1[i]!=s2[i]:
                count+=1
        return count==1
    
    if __name__=='__main__':
        n=int(input())
        beginstr,endstr=map(str,input().split())
        if beginstr==endstr:
            print(0)
            exit()
        strlist=[]
        for i in range(n):
            strlist.append(input())
    
        # use bfs
        visit=[False for i in range(n)]
        queue=[[beginstr,1]]
        while queue:
            str,step=queue.pop(0)
            if judge(str,endstr):
                print(step+1)
                exit()
            for i in range(n):
                if visit[i]==False and judge(strlist[i],str):
                    visit[i]=True
                    queue.append([strlist[i],step+1])
        print(0)
    

你可能感兴趣的:(小白的代码随想录刷题笔记,Mophead的小白刷题笔记,leetcode,python,代码随想录,图论)