[哈希表]966. 元音拼写检查器

966. 元音拼写检查器

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",满足了优先选择靠后单词的需求。

在给定单词列表 wordlist 的情况下,我们希望实现一个拼写检查,将查询单词转换为正确的单词。 对于给定的查询单词 query,拼写检查将会处理两类拼写错误: 大小写:如果查询匹配单词列表中的某个单词(不区分大小写),则返回的正确单词与单词列表中的大小写相同。 例如:wordlist = ["yellow"], query = "YellOw": correct = "yellow" 例如:wordlist = ["Yellow"], query = "yellow": correct = "Yellow" 例如:wordlist = ["yellow"], query = "yellow": correct = "yellow" 元音错误:如果在将查询单词中的元音 ('a', 'e', 'i', 'o', 'u') 分别替换为任何元音后,能与单词列表中的单词匹配(不区分大小写),则返回的正确单词与单词列表中的匹配项大小写相同。 例如:wordlist = ["YellOw"], query = "yollow": correct = "YellOw" 例如:wordlist = ["YellOw"], query = "yeellow": correct = "" (无匹配项) 例如:wordlist = ["YellOw"], query = "yllw": correct = "" (无匹配项) 此外,拼写检查还按照以下优先级规则操作: 当查询完全匹配单词列表中的某个单词(区分大小写)时,应返回相同的单词。 当查询匹配到大小写问题的单词时,您应该返回单词列表中的第一个这样的匹配项。 当查询匹配到元音错误的单词时,您应该返回单词列表中的第一个这样的匹配项。 如果该查询在单词列表中没有匹配项,则应返回空字符串。 给出一些查询 queries,返回一个单词列表 answer,其中 answer[i] 是由查询 query = queries[i] 得到的正确单词。 示例 1: 输入:wordlist = ["KiTe","kite","hare","Hare"], queries = ["kite","Kite","KiTe","Hare","HARE","Hear","hear","keti","keet","keto"] 输出:["kite","KiTe","KiTe","Hare","hare","","","KiTe","","KiTe"] 示例 2: 输入:wordlist = ["yellow"], queries = ["YellOw"] 输出:["yellow"] 提示: 1 <= wordlist.length, queries.length <= 5000 1 <= wordlist[i].length, queries[i].length <= 7 wordlist[i] 和 queries[i] 只包含英文字母 在下面框架中生成 char** spellchecker(char** wordlist, int wordlistSize, char** queries, int queriesSize, int* returnSize) { }
09-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值