【算法】Word Ladder II单词接龙

Word Ladder II 寻找字梯

题目

Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

Only one letter can be changed at a time
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
For example,

Given:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]

Return

[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]
Note:

Return an empty list if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.

给出字梯的头单词和尾单词,以及一组单词,找出相邻单词只有一个字符不同的一组单词
可以假设字符串复合如下条件:

  1. 所有单词长度相同
  2. 所有单词均为小写
  3. 没有重复单词
  4. 头单词和尾单词不为空且不相同

解题思路

  1. 判断单词数组是否含有尾单词,不含有则没有接龙,返回空
  2. 用二维数组实现相邻单词路径图,可一步到达置为true,反之为false
  3. BFS寻找每个节点的前置节点
  4. 用DFS找到从尾节点到头节点的路径并放入容器
  5. 返回容器

代码实现

//
//  AlgorithmManager+WordLadder2.swift
//  EGSwiftLearning
//
//  Created by Easer Liu on 2019/11/19.
//  Copyright © 2019 Easer. All rights reserved.
//

import Foundation
//最终结果的容器
var listList = [[String]]();
//最终结果的临时容器
 var tempPath = [Int]()
//用于存储节点到头节点的步数
 var d = [Int]();
//用于存储节点的前置节点数量
 var countInq = [Int]();
//用于判断节点是否在队列中
 var inq = [Bool]();
//用于存储节点的前置节点序号
 var pre = [Set<Int>]();
extension AlgorithmManager{
    class func WordLadder2Solution(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> [[String]] {
//        将列表和头单词放入Set中
        var hashSet = Set<String>();
        hashSet = hashSet.union(wordList);
        hashSet.insert(beginWord);
//        如果其中不包含尾单词,则不存在单词接龙,直接返回空
        if(!hashSet.contains(endWord)){
            return [[String]]();
        }
        
        //清空容器
        listList.removeAll();
        tempPath.removeAll();
        d.removeAll();
        countInq.removeAll();
        inq.removeAll();
        pre.removeAll();
        
//        将单词加入到list中,并记录头单词和为单词的序号
        var index = 0;
        var start = 0;
        var end = 0;
        var list = [String]()
        for s in hashSet {
            list.append(s)
            if s == beginWord {
                start = index;
            }
            if s == endWord {
                end = index;
            }
            index += 1
        }
//        获得单词的数量,并初始化图,
        let size = list.count;
        var graph = [[Bool]]();
        for i in 0 ..< size{
            graph.append([Bool]())
            d.append(Int.max)
            countInq.append(0)
            pre.append(Set<Int>())
            inq.append(false)
        for _ in 0 ..< size {
            graph[i].append(false)
            }}
//        将图中可一步到达的两个单词位置设置为true
        for i in 0 ..< size {
            for j in i+1 ..< size {
                if self.HasPath(word1: list[i], word2: list[j]) {
                    graph[i][j] = true;
                    graph[j][i] = true;}
            }
        }
//        广度优先,配置图中每个节点的前置节点
        self.BFS(start: start, list: list, graph: graph)
//        深度优先,寻找从尾节点到头节点的路径
        self.DFS(start: start, nowVisit: end, list: list)
        
        
//        返回结果
        print(listList)
                return listList;
    }
//    判断两个单词之间是否可一步到达
    class func HasPath(word1:String, word2:String) -> Bool{
        var count = 0
        let word1Arr = Array(word1)
        let word2Arr = Array(word2)
//        遍历两个单词的字符并对比
        for i in 0 ..< word1.count {
//            若字符不相同,则count+1
            if word1Arr[i] != word2Arr[i] {
                count += 1
            }
        }
//        若count为1,则说明只有一个字符相同,则可以一步到达
        if count == 1 {
            return true
        }
//        否则不能一步到达
        return false
    }
//    广度优先,配置节点的前置节点pre
    class func BFS(start:Int, list:[String], graph:[[Bool]]) -> Bool{
//        单词数量
        let size = list.count;
//        头节点到头节点的j步数为0
        d[start] = 0;
//        初始化队列并将头节点序号放入
        var queue = [Int]()
        queue.append(start)
        var headIndex = 0
        var tailIndex = 1
        let modulo = 10000000000
        countInq[start] += 1;
        inq[start] = true;
//        广度优先算法
        while headIndex != tailIndex {
//            出队节点序号
            let u = queue[headIndex];
            headIndex = (1 + headIndex) % modulo
            inq[u] = false;
//            遍历列队序号设置前置节点
            for v in 0 ..< size {
//                若u节点可一步到达v节点,则配置前置节点
                if(graph[u][v]){
//                    若u节点到头节点的步数+1小于v节点到头节点的步数,则清空v节点的前置节点,并将u节点加入
                    if(d[u] + 1 < d[v]) {
                        pre[v].removeAll();
                        pre[v].insert(u);
                        d[v] = d[u] + 1;
//                        若jv节点没有入队则入队
                        if (!inq[v]) {
                            queue.append(v);
                            tailIndex = (1 + tailIndex) % modulo
                            countInq[v]+=1;
                            inq[v] = true;
                            if (countInq[v] > size - 1) {
                                return false;
                            }
                        }
//                    若u节点到头节点的步数+1等于v节点到头节点的步数,则将u节点加入

                    }else if(d[u] + 1 == d[v]){
                        pre[v].insert(u);
//                        若jv节点没有入队则入队
                        if (!inq[v]) {
                            queue.append(v);
                            tailIndex = (1 + tailIndex) % modulo
                            countInq[v]+=1;
                            inq[v] = true;
                            if (countInq[v] > size - 1) {
                                return false;
                            }
                        }
                    }
                }
            }
        }
        
        return true
    }

//    深度优先,寻找单词接龙
    class func DFS(start:Int, nowVisit:Int, list:[String]) -> (){
//        将当前节点加入临时容器
        tempPath.append(nowVisit)
//        若当前节点为头节点,说明已经找到单词接龙
                if(nowVisit == start){
//                    遍历临时容器,将结果放入到listList
                    var path = [String]()
                    for i in stride(from:tempPath.count-1, through:0, by:-1) {
                        path.append(list[tempPath[i]])
                    }
                    listList.append(path);
//                    删除最后一个节点
                    tempPath.remove(at: tempPath.count - 1);
                    return;
                }
//        遍历当前节点的前置节点,并递归
        for integer in pre[nowVisit] {
            self.DFS(start: start, nowVisit: integer, list: list)
        }
//        删除最后一个节点
                tempPath.remove(at: tempPath.count - 1);
    }
}

最终LeetCode上时间超限,应该还有待改善
代码地址:https://github.com/sinianshou/EGSwiftLearning

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值