|
| 1 | +package Problem0127 |
| 2 | + |
| 3 | +func ladderLength(beginWord string, endWord string, wordList []string) int { |
| 4 | + // 因为 beginWord 不能做 transformed word |
| 5 | + // 先删掉 words 中的 beginWord, |
| 6 | + // 可以让后面的 trans 少很多的断头 path,会加快程序。 |
| 7 | + // 也更符合题意 |
| 8 | + // 删除下面这句,程序也能 accepted,但是会从 269ms 减慢到 319ms |
| 9 | + words = deleteBeginWord(words, beginWord) |
| 10 | + |
| 11 | + // trans 用来查找 k->? |
| 12 | + trans := map[string][]string{} |
| 13 | + // isTransedEndWord 用于在生成 trans 的过程中标记,存在 ->endWord 的转换关系 |
| 14 | + // 用于提前结束 |
| 15 | + isTransedEndWord := false |
| 16 | + // cnt 用于记录生成trans的迭代次数 |
| 17 | + // 其实也是最短路径的长度 |
| 18 | + cnt := 1 |
| 19 | + var bfs func([]string, []string) |
| 20 | + // 使用 bfs 方法,递归地生成 trans |
| 21 | + bfs = func(words, nodes []string) { |
| 22 | + cnt++ |
| 23 | + // words 中的 w |
| 24 | + // 与 nodes 中的 n ,可以实现 n->w 的转换, |
| 25 | + // 则,w 会被放入 newNodes |
| 26 | + // 否则,w 会被放入 newWords |
| 27 | + newWords := make([]string, 0, len(words)) |
| 28 | + newNodes := make([]string, 0, len(words)) |
| 29 | + for _, w := range words { |
| 30 | + isTransed := false |
| 31 | + for _, n := range nodes { |
| 32 | + if isTransable(n, w) { |
| 33 | + trans[n] = append(trans[n], w) |
| 34 | + isTransed = true |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + if isTransed { |
| 39 | + newNodes = append(newNodes, w) |
| 40 | + if w == endWord { |
| 41 | + isTransedEndWord = true |
| 42 | + } |
| 43 | + } else { |
| 44 | + newWords = append(newWords, w) |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + if isTransedEndWord || // 转换到了 endWord 说明已经找到了所有的最短路径 |
| 49 | + len(newWords) == 0 || // words 的所有单词都已经可以从 beginWord trans 到 |
| 50 | + len(newNodes) == 0 { // newWords 中单词,是 beginWord 无法 trans 到的 |
| 51 | + return |
| 52 | + } |
| 53 | + |
| 54 | + // 上面没有 return,要继续完善 trans |
| 55 | + bfs(newWords, newNodes) |
| 56 | + } |
| 57 | + |
| 58 | + // 第一代 nodes 含有且仅含有 beginWord |
| 59 | + nodes := []string{beginWord} |
| 60 | + bfs(words, nodes) |
| 61 | + |
| 62 | + res := [][]string{} |
| 63 | + if !isTransedEndWord { |
| 64 | + // beginWord 无法 trans 到 endWord |
| 65 | + return res |
| 66 | + } |
| 67 | + |
| 68 | + path := make([]string, cnt) |
| 69 | + path[0] = beginWord |
| 70 | + |
| 71 | + var dfs func(int) |
| 72 | + // 使用 dfs 方法,生成最短路径 |
| 73 | + dfs = func(idx int) { |
| 74 | + if idx == cnt { |
| 75 | + // path 已经填充完毕 |
| 76 | + if path[idx-1] == endWord { |
| 77 | + // 最后一个单词是 endWord,说明这是一条最短路径 |
| 78 | + res = append(res, deepCopy(path)) |
| 79 | + } |
| 80 | + return |
| 81 | + } |
| 82 | + |
| 83 | + prev := path[idx-1] |
| 84 | + for _, w := range trans[prev] { |
| 85 | + // 利用 prev->w 填充 path[idx] |
| 86 | + path[idx] = w |
| 87 | + dfs(idx + 1) |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + dfs(1) |
| 92 | + |
| 93 | + return res |
| 94 | +} |
| 95 | + |
| 96 | +func deepCopy(src []string) []string { |
| 97 | + temp := make([]string, len(src)) |
| 98 | + copy(temp, src) |
| 99 | + return temp |
| 100 | +} |
| 101 | + |
| 102 | +// 题目中说了,words 中没有重复的单词, |
| 103 | +// 所以,beginWord 最多出现一次 |
| 104 | +func deleteBeginWord(words []string, beginWord string) []string { |
| 105 | + i := 0 |
| 106 | + for ; i < len(words); i++ { |
| 107 | + if words[i] == beginWord { |
| 108 | + break |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + if i == len(words) { |
| 113 | + return words |
| 114 | + } |
| 115 | + |
| 116 | + return append(words[:i], words[i+1:]...) |
| 117 | +} |
| 118 | + |
| 119 | +func isTransable(a, b string) bool { |
| 120 | + // onceAgain == true 说明已经出现过不同的字符了 |
| 121 | + onceAgain := false |
| 122 | + for i := range a { |
| 123 | + if a[i] != b[i] { |
| 124 | + if onceAgain { |
| 125 | + return false |
| 126 | + } |
| 127 | + onceAgain = true |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + return true |
| 132 | +} |
0 commit comments