-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path127.单词接龙.ts
More file actions
159 lines (139 loc) · 3.9 KB
/
127.单词接龙.ts
File metadata and controls
159 lines (139 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
* @lc app=leetcode.cn id=127 lang=typescript
*
* [127] 单词接龙
*
* https://leetcode.cn/problems/word-ladder/description/
*
* algorithms
* Hard (47.97%)
* Likes: 1133
* Dislikes: 0
* Total Accepted: 165.9K
* Total Submissions: 345.7K
* Testcase Example: '"hit"\n"cog"\n["hot","dot","dog","lot","log","cog"]'
*
* 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 ->
* s2 -> ... -> sk:
*
*
* 每一对相邻的单词只差一个字母。
* 对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
* sk == endWord
*
*
* 给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列
* 中的 单词数目 。如果不存在这样的转换序列,返回 0 。
*
*
* 示例 1:
*
*
* 输入:beginWord = "hit", endWord = "cog", wordList =
* ["hot","dot","dog","lot","log","cog"]
* 输出:5
* 解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
*
*
* 示例 2:
*
*
* 输入:beginWord = "hit", endWord = "cog", wordList =
* ["hot","dot","dog","lot","log"]
* 输出:0
* 解释:endWord "cog" 不在字典中,所以无法进行转换。
*
*
*
* 提示:
*
*
* 1 <= beginWord.length <= 10
* endWord.length == beginWord.length
* 1 <= wordList.length <= 5000
* wordList[i].length == beginWord.length
* beginWord、endWord 和 wordList[i] 由小写英文字母组成
* beginWord != endWord
* wordList 中的所有字符串 互不相同
*
*
*/
export
// @lc code=start
function ladderLength(beginWord: string, endWord: string, wordList: string[]): number {
const graph = buildGraph([beginWord, ...wordList])
if (!graph[endWord]?.size) return 0
const frontQueue = [beginWord]
const frontSteps = new Map<string, number>()
const endQueue = [endWord]
const endSteps = new Map<string, number>()
let frontCount = 0
let endCount = 0
while (frontQueue.length || endQueue.length) {
while (frontQueue.length) {
frontCount++
const levelLength = frontQueue.length
for (let i = 0; i < levelLength; i++) {
const word = frontQueue.shift()!
if (endSteps.has(word)) {
return frontCount + endSteps.get(word)!
}
for (const next of graph[word]) {
if (!frontSteps.has(next)) {
frontSteps.set(next, frontCount)
frontQueue.push(next)
}
}
}
}
while (endQueue.length) {
endCount++
const levelLength = endQueue.length
for (let i = 0; i < levelLength; i++) {
const word = endQueue.shift()!
if (frontSteps.has(word)) {
return endCount + frontSteps.get(word)!
}
for (const next of graph[word]) {
if (!endSteps.has(next)) {
endSteps.set(next, endCount)
endQueue.push(next)
}
}
}
}
}
return 0
}
function buildGraph(wordList: string[]) {
const hash2Words: Record<string, Set<string>> = {}
const word2Hash: Record<string, Set<string>> = {}
for (const word of wordList) {
word2Hash[word] = new Set()
for (const hash of getHashes(word)) {
hash2Words[hash] = hash2Words[hash] || new Set()
hash2Words[hash].add(word)
word2Hash[word].add(hash)
}
}
const result: Record<string, Set<string>> = {}
for (const word of wordList) {
result[word] = new Set()
for (const hash of word2Hash[word]) {
for (const n of hash2Words[hash]) {
result[word].add(n)
}
}
result[word].delete(word)
}
return result
}
function getHashes(word: string) {
const result: string[] = []
for (let i = 0; i < word.length; i++) {
result.push(`${word.slice(0, i)}?${word.slice(i + 1, word.length)}`)
}
return result
}
// @lc code=end
export {}