我的任务是为作业创建一个简单的拼写检查程序,但是旁边没有任何指导,所以想知道是否有人可以帮助我。我不是在跟别人为我做任务,但算法的任何方向或帮助都会很棒!如果我要求的不在网站的guildlines内,那么我很抱歉,我会在其他地方看看。 :)
项目正确加载拼写小写单词,然后根据两个标准制作拼写建议:
一个字母差异(加上或减去以使单词与字典中的单词相同)。例如,'stack'将是'staick'的建议,'cool'将是'coo'的建议。
一个字母替换。因此,例如,'bad'将是对'bod'的建议。
所以,只是为了确保我已经正确解释了......我可能会加载[你好,再见,太棒了,好,上帝]的字样然后对(错误拼写的)'godd'这个词的建议将是[好的,上帝]。
速度是我的主要考虑因素,所以当我认为我知道一种方法来完成这项工作时,我真的不太确定它的效率。我正在考虑这样做的方法是创建一个map<string, vector<string>>
,然后为每个正确拼写的单词加载,将正确拼写的作品添加为地图中的一个键,并将向量填充为全部这个词的可能“错误”排列。
然后,当我想查找一个单词时,我会查看地图中的每个向量,看看该单词是否是正确拼写单词之一的排列。如果是,我会将密钥添加为拼写建议。
这似乎会占用内存的HEAPS,因为每个单词肯定会有成千上万的排列?如果我的正确拼写单词的初始词典很大,它似乎也会非常慢?
我在想,也许我可以通过查看与我正在看的单词相似的键来缩短时间。但话说回来,如果它们在某种程度上相似,那么它可能意味着关键将是一个建议意味着我不需要所有这些排列!
所以是的,我有点难过我应该看哪个方向。我真的很感激任何帮助,因为我真的不知道如何估计不同做事方式的速度(我们还没有在课堂上一直教这个。
答案 0 :(得分:6)
解决问题的简单方法确实是预先计算好的地图[坏词] - &gt; [建议]。
问题在于,虽然删除一个字母会产生很少的“坏词”,但对于添加或替换,你有许多候选人。
所以我建议另一个解决方案;)
注意:您描述的编辑距离称为Levenshtein Distance
解决方案以增量步骤描述,通常搜索速度应该在每个想法中不断改进,并且我尝试首先使用更简单的想法(在实现方面)组织它们。每当您对结果感到满意时,请随时停下来。
0。初步
std::set
,但排序的std::deque
或std::vector
会更好地表现性能)关键点:
后一个属性允许短路实现:如果您想将自己限制为2个错误(阈值),那么只要当前行的最小值优于2,您就可以放弃计算。一个简单的策略是将treshold + 1作为距离返回。
1。第一个暂定
让我们开始吧。
我们将实现线性扫描:对于每个单词,我们计算距离(短路),然后列出那些到目前为止距离较小的单词。
它适用于小词典。
2。改善数据结构
levenshtein距离至少等于长度的差异。
通过将夫妻(长度,单词)用作关键字而不仅仅是单词,您可以将搜索范围限制在[length - edit, length + edit]
的范围内,并大大减少搜索空间。
3。前缀和修剪
为了改进这一点,我们可以说,当我们逐行构建距离矩阵时,一个世界被完全扫描(我们寻找的那个)但另一个(指示物)不是:我们只使用一个字母对于每一行。
这个非常重要的属性意味着对于共享相同初始序列(前缀)的两个指示对象,则矩阵的第一行将是相同的。
还记得我要求你存储字典吗?这意味着共享相同前缀的单词是相邻的。
假设您正在针对cartoon
和car
检查您的单词,您发现它不起作用(距离已经太长),那么任何以car
开头的单词都会赢得'无论如何,您都可以跳过单词,只要它们以car
开头。
跳过本身可以线性完成或通过搜索完成(找到前缀高于car
的第一个单词):
“长”的时间长短取决于你的字典,你必须衡量。我会继续二进制搜索。
注意:长度分区对前缀分区起作用,但它会修剪更多的搜索空间
4。前缀和重复使用
现在,我们也会尝试尽可能多地重复使用计算(而不仅仅是“它不起作用”的结果)
假设您有两个单词:
首先逐行计算cartoon
的矩阵。然后,在阅读carwash
时,您需要确定公共前缀的长度(此处为car
),并且您可以保留矩阵的前4行(对应于void,c
,{{ 1}},a
)。
因此,当开始计算r
时,您实际上开始在carwash
进行迭代。
要做到这一点,只需使用在搜索开始时直接分配的数组,并使其足够大以容纳更大的引用(您应该知道字典中最大的长度)。
5。使用“更好”的数据结构
为了更轻松地使用前缀,您可以使用Trie或Patricia树来存储字典。然而,它不是STL数据结构,您需要对其进行扩充以在每个子树中存储存储的字长度范围,因此您必须自己实现。它并不像看起来那么容易,因为存在可能会破坏局部性的内存爆炸问题。
这是最后的选择。实施成本很高。
答案 1 :(得分:4)
你应该看一下Peter Norvig关于如何编写拼写纠错器的解释。
How to write a spelling corrector
本文很好地解释了EveryThing,作为一个例子,拼写检查器的python代码如下所示:
import re, collections
def words(text): return re.findall('[a-z]+', text.lower())
def train(features):
model = collections.defaultdict(lambda: 1)
for f in features:
model[f] += 1
return model
NWORDS = train(words(file('big.txt').read()))
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def edits1(word):
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [a + b[1:] for a, b in splits if b]
transposes = [a + b[1] + b[0] + b[2:] for a, b in splits if len(b)>1]
replaces = [a + c + b[1:] for a, b in splits for c in alphabet if b]
inserts = [a + c + b for a, b in splits for c in alphabet]
return set(deletes + transposes + replaces + inserts)
def known_edits2(word):
return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)
def known(words): return set(w for w in words if w in NWORDS)
def correct(word):
candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
return max(candidates, key=NWORDS.get)
希望您能在Peter Norvig网站上找到您需要的东西。
答案 2 :(得分:2)
对于拼写检查,许多数据结构对BK-Tree有用。检查Damn Cool Algorithms, Part 1: BK-Trees我已完成相同here
的实施我的早期代码链接可能会产生误导,this one对于拼写纠错器是正确的。
答案 3 :(得分:0)
离开我的头顶,你可以根据长度分割你的建议并建立一个树结构,其中孩子是较短父母的较长变体。
应该很快,但我不确定代码本身,我不是很精通c ++