class Solution:
def spellchecker(self, wordlist: List[str], queries: List[str]) -> List[str]:
origin = set(wordlist) # 存储原始单词用于完全匹配
lower_to_origin = {} # 存储小写形式到原始单词的映射
vowel_to_origin = {} # 存储元音模糊形式到原始单词的映射
trans = str.maketrans("aeiou", "?????") # 创建元音替换表(小写)
# 构建映射字典(优先保留先出现的单词)
for s in wordlist:
lower_s = s.lower()
# 小写形式映射:仅当键不存在时添加
if lower_s not in lower_to_origin:
lower_to_origin[lower_s] = s
# 元音模糊形式映射:先转为小写再替换元音
vowel_s = lower_s.translate(trans)
if vowel_s not in vowel_to_origin:
vowel_to_origin[vowel_s] = s
# 处理每个查询
res = []
for q in queries:
# 1. 完全匹配(区分大小写)
if q in origin:
res.append(q)
continue
lower_q = q.lower()
# 2. 不区分大小写匹配
if lower_q in lower_to_origin:
res.append(lower_to_origin[lower_q])
continue
# 3. 元音模糊匹配
vowel_q = lower_q.translate(trans)
if vowel_q in vowel_to_origin:
res.append(vowel_to_origin[vowel_q])
else:
res.append("") # 无匹配返回空字符串
return res
1. 类和方法的定义
spellchecker
方法,该方法的主要功能是实现一个拼写检查器。它接收两个参数:一个单词列表 wordlist
和一个查询列表 queries
,并返回一个与 queries
长度相同的列表,列表中的每个元素是对相应查询的最佳匹配结果
2. 初始化数据结构
origin = set(wordlist)
lower_to_origin = {}
vowel_to_origin = {}
trans = str.maketrans("aeiou", "?????") # 替换元音为 '?'
origin
:将wordlist
转换为集合,用于快速检查某个单词是否在原始单词列表中,因为集合的查找操作时间复杂度为 O(1)。lower_to_origin
:一个字典,用于存储小写形式的单词到原始单词的映射,方便进行不区分大小写的匹配。vowel_to_origin
:一个字典,用于存储将元音字母替换为'?'
后的小写单词到原始单词的映射,用于不区分大小写且元音模糊匹配。trans
:使用str.maketrans()
方法创建一个字符映射转换表,将元音字母'a'
、'e'
、'i'
、'o'
、'u'
替换为'?'
。
3. 遍历单词列表,构建映射关系
for s in reversed(wordlist):
lower = s.lower()
lower_to_origin[lower] = s # 例如 kite -> KiTe
vowel_to_origin[lower.translate(trans)] = s # 例如 k?t? -> KiTe
- 使用
reversed(wordlist)
反向遍历单词列表,这样在有多个匹配时,优先选择单词列表中靠后的单词。 lower = s.lower()
:将当前单词转换为小写形式。lower_to_origin[lower] = s
:将小写形式的单词作为键,原始单词作为值存入lower_to_origin
字典,用于不区分大小写的匹配。lower.translate(trans)
:使用之前创建的字符映射转换表trans
,将小写单词中的元音字母替换为'?'
。然后将替换后的单词作为键,原始单词作为值存入vowel_to_origin
字典,用于不区分大小写且元音模糊匹配。
4. 遍历查询列表,进行匹配操作
for i, q in enumerate(queries):
if q in origin: # 完全匹配
continue
lower = q.lower()
if lower in lower_to_origin: # 不区分大小写的匹配
queries[i] = lower_to_origin[lower]
else: # 不区分大小写+元音模糊匹配
queries[i] = vowel_to_origin.get(lower.translate(trans), "")
- 使用
enumerate(queries)
同时获取查询单词的索引i
和单词q
。 if q in origin:
:检查查询单词是否在原始单词列表中,如果是,则不做处理,继续处理下一个查询。lower = q.lower()
:将查询单词转换为小写形式。if lower in lower_to_origin:
:检查小写形式的查询单词是否在lower_to_origin
字典中,如果是,则将查询结果替换为对应的原始单词。else:
:如果不满足上述条件,则进行不区分大小写且元音模糊匹配。使用lower.translate(trans)
将小写查询单词中的元音字母替换为'?'
,然后使用vowel_to_origin.get()
方法查找对应的原始单词。如果找到则替换查询结果,否则将查询结果替换为空字符串。
5. 返回结果
return queries
最后返回经过匹配处理后的查询列表。
示例代码运行
from typing import List
class Solution:
def spellchecker(self, wordlist: List[str], queries: List[str]) -> List[str]:
origin = set(wordlist)
lower_to_origin = {}
vowel_to_origin = {}
trans = str.maketrans("aeiou", "?????") # 替换元音为 '?'
for s in reversed(wordlist):
lower = s.lower()
lower_to_origin[lower] = s # 例如 kite -> KiTe
vowel_to_origin[lower.translate(trans)] = s # 例如 k?t? -> KiTe
for i, q in enumerate(queries):
if q in origin: # 完全匹配
continue
lower = q.lower()
if lower in lower_to_origin: # 不区分大小写的匹配
queries[i] = lower_to_origin[lower]
else: # 不区分大小写+元音模糊匹配
queries[i] = vowel_to_origin.get(lower.translate(trans), "")
return queries
# 示例调用
solution = Solution()
wordlist = ["KiTe", "kite", "hare", "Hare"]
queries = ["kite", "Kite", "KiTe", "Hare", "HARE", "Hear", "hear", "keti", "keet", "keto"]
result = solution.spellchecker(wordlist, queries)
print(result)
这段代码通过构建不同的映射关系,实现了三种类型的拼写匹配:完全匹配、不区分大小写的匹配和不区分大小写且元音模糊匹配。
为什么要用 origin = set (wordlist)
在这段代码里使用 origin = set(wordlist)
主要是为了利用集合(set
)这种数据结构的特性,来优化单词完全匹配的查找效率,下面从集合特性、查找效率、代码逻辑等方面详细解释:
1. 集合(set
)的特性
在 Python 中,集合是一种无序且元素唯一的数据结构。当把列表 wordlist
转换为集合 origin
时,会自动去除其中重复的元素,并且集合的存储方式使得元素的查找操作非常高效。
2. 提高查找效率
在代码中,需要对查询列表 queries
中的每个查询单词进行完全匹配检查,也就是判断某个查询单词是否在 wordlist
中。如果直接使用列表 wordlist
进行查找操作,例如使用 if q in wordlist
,其时间复杂度是 O(n),这里的 n 是 wordlist
的长度。因为在列表中查找元素时,需要逐个遍历列表中的元素,直到找到匹配的元素或者遍历完整个列表。
而集合的查找操作时间复杂度是 O(1)。这是因为集合使用了哈希表来存储元素,通过哈希函数可以直接计算出元素在哈希表中的存储位置,从而快速判断元素是否存在。所以,将 wordlist
转换为集合 origin
后,使用 if q in origin
进行查找,能显著提高查找效率,尤其是当 wordlist
长度较大时,性能提升会更加明显。
3. 结合代码逻辑的使用
在 spellchecker
方法中,代码首先进行完全匹配检查:
for i, q in enumerate(queries):
if q in origin: # 完全匹配
continue
这里使用集合 origin
来判断查询单词 q
是否在原始单词列表中。如果存在完全匹配的情况,就不做任何处理,直接继续处理下一个查询单词。由于使用了集合,这个查找操作能快速完成,避免了使用列表带来的性能开销。
示例对比
下面通一个简单的示例对比使用列表和集合进行查找操作的时间差异:
import time
# 生成一个较长的单词列表
wordlist = [str(i) for i in range(1000000)]
# 使用列表进行查找
start_time = time.time()
for _ in range(1000):
if '999999' in wordlist:
pass
end_time = time.time()
print(f"使用列表查找耗时: {end_time - start_time} 秒")
# 将列表转换为集合
origin = set(wordlist)
# 使用集合进行查找
start_time = time.time()
for _ in range(1000):
if '999999' in origin:
pass
end_time = time.time()
print(f"使用集合查找耗时: {end_time - start_time} 秒")
#使用列表查找耗时: 6.8081955909729 秒
#使用集合查找耗时: 0.0 秒
在这个示例中,当 wordlist
长度较大时,可以明显看到使用集合进行查找的速度比使用列表快很多。
综上所述,使用 origin = set(wordlist)
是为了利用集合高效的查找特性,优化代码中完全匹配检查的性能。
为什么这里用 reversed 方法
在遍历 wordlist
时,代码会将小写单词及其元音替换后的形式作为键,原始单词作为值,更新 lower_to_origin
和 vowel_to_origin
这两个字典:
for s in reversed(wordlist):
lower = s.lower()
lower_to_origin[lower] = s # 例如 kite -> KiTe
vowel_to_origin[lower.translate(trans)] = s # 例如 k?t? -> KiTe
在 Python 字典中,如果多次使用相同的键进行赋值操作,后面的赋值会覆盖前面的赋值。因此,当反向遍历 wordlist
时,对于相同的小写形式或者元音替换后的形式,靠后的原始单词会最终被存储在字典中。
假设 wordlist = ["KiTe", "kite"]
,查询单词为 "kite"
,在不区分大小写的匹配情况下,"KiTe"
和 "kite"
都能匹配。如果正向遍历 wordlist
,先处理 "KiTe"
,lower_to_origin["kite"]
会被赋值为 "KiTe"
,接着处理 "kite"
时,lower_to_origin["kite"]
会被更新为 "kite"
。
而使用 reversed(wordlist)
反向遍历,先处理 "kite"
,lower_to_origin["kite"]
被赋值为 "kite"
,再处理 "KiTe"
时,由于字典键的唯一性,lower_to_origin["kite"]
不会被更新,最终 lower_to_origin["kite"]
的值就是 "kite"
,满足了优先选择靠后单词的需求。