Skip to content

Commit c58fbcc

Browse files
aQuaaQua
authored andcommitted
127 added
1 parent 0ecd3bf commit c58fbcc

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# [127. Word Ladder](https://leetcode.com/problems/word-ladder/)
2+
3+
## 题目
4+
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
5+
1. Only one letter can be changed at a time.
6+
1. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
7+
```
8+
For example,
9+
Given:
10+
beginWord = "hit"
11+
endWord = "cog"
12+
wordList = ["hot","dot","dog","lot","log","cog"]
13+
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
14+
return its length 5.
15+
```
16+
Note:
17+
1. Return 0 if there is no such transformation sequence.
18+
1. All words have the same length.
19+
1. All words contain only lowercase alphabetic characters.
20+
1. You may assume no duplicates in the word list.
21+
1. You may assume beginWord and endWord are non-empty and are not the same.
22+
23+
UPDATE (2017/1/20):
24+
The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.
25+
26+
## 解题思路
27+
28+
见程序注释
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package Problem0127
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func Test_Problem0127(t *testing.T) {
11+
ast := assert.New(t)
12+
13+
// tcs is testcase slice
14+
tcs := []struct {
15+
beginWord string
16+
endWord string
17+
wordList []string
18+
ans int
19+
}{
20+
21+
{
22+
"hit",
23+
"cog",
24+
[]string{"hot", "dot", "dog", "lot", "log"},
25+
0,
26+
},
27+
28+
{
29+
"hot",
30+
"dog",
31+
[]string{"hot", "dog", "dot"},
32+
3,
33+
},
34+
35+
{
36+
"hit",
37+
"cog",
38+
[]string{"hot", "dot", "dog", "lot", "log", "cog"},
39+
5,
40+
},
41+
42+
// 可以多个 testcase
43+
}
44+
45+
for _, tc := range tcs {
46+
fmt.Printf("~~%v~~\n", tc)
47+
48+
ast.Equal(tc.ans, ladderLength(tc.beginWord, tc.endWord, tc.wordList), "输入:%v", tc)
49+
}
50+
}

0 commit comments

Comments
 (0)