查找列表中所有与单个字母不同的单词
Posted
技术标签:
【中文标题】查找列表中所有与单个字母不同的单词【英文标题】:Find all the words in the list that differ by a single letter 【发布时间】:2020-08-21 22:41:21 【问题描述】:对于列表words
中的任何给定单词w
,我想通过更改其中的单个字母来查找列表中可以变为w
的所有其他单词。所有单词的长度相等,只允许替换。调用这个函数parent(w)
。
例如,给定words = ["hot","dot","dog","lot","log","cog"]
,parent("cog")
将是["dog", "log"]
。 parent("lot")
将是 ["dot", "hot", "log"]
等等。
为此,我首先构建了一个反向索引,其中键 (str, int)
映射到索引 int
处具有字符 str
的单词。然后,找到一个单词的双亲就变成了将所有与该单词在相同位置具有相同字母的单词相交的任务,除了一个。
代码如下,产生一个空集。为什么它不起作用?
from typing import Iterator, Dict, Tuple, Set
import itertools
graph: Dict[Tuple[str, int], Set[int]] = dict()
for i, word in enumerate(words):
for j, ch in enumerate(word):
if (ch, j) not in graph:
graph[(ch, j)] = set()
graph[(ch, j)].add(i)
def parents(word: str) -> Iterator[int]:
n: int = len(word)
s: Set[int] = set()
for part in itertools.combinations(range(n), n - 1):
keys = map(lambda x: (word[x], x), part)
existing_keys = filter(lambda k: k in graph, keys)
for y in itertools.chain(map(lambda k: graph[k], existing_keys)):
s = s.intersection(set(y)) if s else set(y)
return filter(lambda i: words[i] != word, s)
print(list(parents("cog"))) # empty!!!
【问题讨论】:
itertools.chain
应该做什么?现在它什么也没做。
你能用difflib
吗?见get_close_matches
:docs.python.org/3/library/…
@ekhumoro words = ["log"]; word = "olx"; print([w for w in words if len(set(word) - set(w)) == 1])
@JakubDąbek [w for w in words if len(set(enumerate(word)) - set(enumerate(w))) == 1]
?
@ekhumoro 对于长度相等的单词,这看起来是一个正确的解决方案:D 这基本上是 OP 代码的简化版本,没有中间结构。根据查找频率和数据,总体速度可能会更快。
【参考方案1】:
您的解决方案几乎就在那里。问题是你正在与你找到的所有东西相交。但是,您应该为每个组合附加结果。将s: Set[int] = set()
移动到第一个 for 循环中,并将结果附加到第二个 for 循环之后,它会起作用。像这样的:
def parents(word: str) -> Set[int]:
ret: Set[int] = set()
for part in itertools.combinations(range(n), n - 1):
keys = map(lambda x: (word[x], x), part)
existing_keys = filter(lambda k: k in graph, keys)
s: Set[int] = set()
for y in map(lambda k: graph[k], existing_keys):
s = s.intersection(set(y)) if s else set(y)
ret.update(filter(lambda i: words[i] != word, s))
return ret
【讨论】:
@AbhijitSarkar 检查图中是否存在键是多余的。试试parents("xxg")
,因为它绕过了后面的检查。使用(graph.get(k, set()) for k in keys)
做你想做的事,当那个位置没有单词时替换一个空集。
不适用于words = ["a", "b", "c"]
和word = "a"
。 This answer 确实如此。
@AbhijitSarkar 也可以只做if len(word) <= 1: return set(range(len(words)))
,因为它是算法中的一个特例。【参考方案2】:
一个非常简单的解决方案。一种不同的方法。
复杂度:O(N * 26) => O(N)
- 其中 N 是每个单词中的字符数。
def main(words, word):
words = set(words)
res = []
for i, _ in enumerate(word):
for c in 'abcdefghijklmnopqrstuvwxyz':
w = word[:i] + c + word[i+1:]
if w != word and w in words:
res.append(w)
return res
print(main(["hot","dot","dog","lot","log","cog"], "cog"))
# ['dog', 'log']
您也可以选择仅对列表中出现的字母进行迭代,而不是遍历所有字母:
letter for w in words for letter in w
【讨论】:
从技术上讲,我创建的 DS 不是 trie,因为它没有根。 您的代码稍微简单一些,但您假设只使用小写字母。问题没有这么说。如果这是一个面试问题(它实际上是面试问题的一部分),你会被搞砸的。另请注意,您执行多个子字符串操作,而在我的代码中,我利用了单词长度相等的事实,因此组合的数量是恒定的。 还有一种方法可以只处理列表中的字母。但是,是的,对于第一个解决方案,假设是正确的。 事实证明,我的代码不适用于words = ["a", "b", "c"]
和 word = "a".
我会接受您的正确回答。请注意,您可以将abcdefghijklmnopqrstuvwxyz
替换为string.ascii_lowercase
;
O(N * K)
,K
是什么? N
我猜是字长。【参考方案3】:
Levenshtein 距离算法将实现您所寻找的。p>
from Levenshtein import distance # pip install python-Levenshtein
words = ["hot", "dot", "dog", "lot", "log", "cog"]
parent = 'cog'
# find all words matching with one substitution
edits = [w for w in words if distance(parent, w) == 1]
print(edits)
输出:
['dog', 'log']
如果您不想安装任何库,有很好的online resources 与算法的 Python 实现。
【讨论】:
我知道 Levenshtein 算法,虽然我不知道 Python 模块存在于此。不过,我宁愿不安装其他依赖项。我的问题中的算法应该如图所示工作,问题是真的,为什么它不?【参考方案4】:我会使用 Python in 函数检查父单词 w 的每个字母与列表中的每个单词。
例如针对单词列表的parent("cog")
:
["hot","dot","dog","lot","log","cog"]
产量:
[1, 1, 2, 1, 2, 3]
数字 2 显示正确的词:dog 和 log。
【讨论】:
以上是关于查找列表中所有与单个字母不同的单词的主要内容,如果未能解决你的问题,请参考以下文章
leetcode 524. 通过删除字母匹配到字典里最长单词双指针,在不同字符串中同向查找
leetcode 524. 通过删除字母匹配到字典里最长单词双指针,在不同字符串中同向查找
leetcode 524. 通过删除字母匹配到字典里最长单词双指针在不同字符串中同向查找