单词接龙:差一个字母就是一步
词是点,只改一个字母且新词在表里就是边。无权图的最短路用 BFS,第一次碰到终点的层数就是答案。
wordList 建集合。从 beginWord 出发 BFS:弹出词,逐位替换成 a–z 得到候选,若在词表中且未访问则入队。第一次到达 endWord 时的层数即答案。时间 O(n·L·26),空间 O(n)。
给定 beginWord、endWord 和一本词典 wordList。每次只能改当前词的一个字母,改完必须仍在词典里(起点可以不在词典里),求从起点变到终点的最短序列长度,含两端;变不到返回 0。
主例 begin=hit,end=cog,词表 [hot,dot,dog,lot,log,cog]。一条最短链是 hit→hot→dot→dog→cog,长度 5。演示从第 1 步的 hit,扩到 hot,再扩到 dot/lot,第 5 步碰到 cog。
DFS 乱改也能碰到 cog,但不保证最短。本文要回答:为什么「差一位」构成无权边,以及主例里为什么第 5 层才是第一次合法到达,而不是第 3 层硬改 hit 的三个字母。
缺口是最短层数,不是任意一条变形链
第一直觉从 hit 出发,把某个字母改成别的,看新词在不在表里。可以改成 hot,再改成 dot 或 lot,继续下去。若用 DFS 先钻一条很长的弯路,先碰到 cog 的长度可能大于 5。题目要的是最短,不是任意合法链。
把每个词看成点。两个词长度相同且恰好差一个字母,之间有一条边,边权都是 1。最短转换长度就是这条隐式图上的最短路点数(含起点)。无权图第一次到达终点的 BFS 层数就是最短,不必 Dijkstra。
邻居不必预先建图。弹出一个词,对每一位试 a 到 z,共 26L 个候选;在集合里且没访问过就入队。访问过的词立刻标记,避免再从别的路径以更长的步数走进来。终点若不在词表里,一开始就可以返回 0——最后一步也必须落在表内。
用手走主例。步 1 队列里只有 hit。hit 三位分别乱改,词表里只有 hot 合法,步 2 前沿是 [hot]。hot 能变出 dot 和 lot(hat、hop 等不在表里),步 3 前沿 [dot, lot],与演示一致。下一层 dog、log,再下一层两边都能改出 cog。步 5 第一次弹出 cog,返回 5。演示跳过了第 4 层的 dog/log,直接标出 cog 命中。
为什么不能第 3 步从 hit 一步改三个字母到 cog?边的定义是「恰好改一个」。hit 和 cog 差了三个位置,中间必须有合法跳板。双向 BFS 从两端同时扩,冲突层相加也能得到 5,词表很大时更快;教学上单向层序已经把「最短」说清楚。
队列空了还没碰到终点,返回 0。每个词只入队一次,复杂度是词数 × 词长 × 26。不要对整本词典两两比汉明距离建图——那是 O(n² L),词一多先炸在建图上。
邻居生成无权边 = 恰好差一个字符。逐位枚举 26 个字母是生成邻居的办法,不是「把词改成任意长度」。BFS 的层数含 beginWord,主例 hit 算第 1 步,cog 是第 5 步。
Go:BFS 逐位替换
func ladderLength(beginWord, endWord string, wordList []string) int {words := map[string]bool{}for _, w := range wordList { words[w] = true }if !words[endWord] { return 0 }q := []string{beginWord}visited := map[string]bool{beginWord: true}steps := 1for len(q) > 0 {steps++size := len(q)for i := 0; i < size; i++ {cur := q[0]; q = q[1:]b := []byte(cur)for p := 0; p < len(b); p++ {for c := byte('a'); c <= 'z'; c++ {b[p] = cnb := string(b)if nb == endWord { return steps }if words[nb] && !visited[nb] {visited[nb] = trueq = append(q, nb)}}b[p] = cur[p]}}}return 0}
1词表进集合,查询 O(1)。终点不在表里直接 0,主例 cog 在表里才继续。
2steps 从 1 起,进入新一层先加一。从 hit 扩到 hot 时 steps 变成 2,与演示一致。
3按层消化 size 个词,保证 steps 是层数不是队列总长度。
4对每一位试 26 个字母。改完立刻查是不是 endWord,主例在 steps=5 命中 cog。
5b[p] 改完必须写回原字符,否则下一位是在脏串上改。访问过的词不再入队。
总结
差一位是一条边,BFS 层数是最短链长。主例 hit→…→cog 答案 5。
- DFS 能到但不保证最短。无权图第一次到达即最短。
- 邻居用逐位替换生成,不要 O(n²) 两两建图。
- 终点必须在词表里。长度含 beginWord,hit 算第 1 步。