解决问题的简单方法确实是预先计算好的地图[坏词] – > [建议].
问题在于,虽然删除一个字母会产生很少的“坏词”,但对于添加或替换,你有很多候选人.
所以我建议另一个解决方案;)
解决方案以增量步骤描述,通常搜索速度应该在每个想法中不断改进,并且我尝试首先使用更简单的想法(在实现方面)来组织它们.每当您对结果感到满意时,请随时停下来.
0.初步
>实施Levenshtein距离算法
>将字典存储在排序的序列中(例如std :: set,尽管排序的std :: deque或std :: vector会更好地表现性能)
关键点:
> Levenshtein距离计算使用数组,在每一步中,下一行仅使用前一行计算
>行中的最小距离始终优于(或等于)前一行中的最小值
后一个属性允许短路实现:如果您想将自己限制为2个错误(阈值),那么只要当前行的最小值优于2,您就可以放弃计算.一个简单的策略是将阈值1作为距离返回.
1.第一个暂定
让我们开始吧.
我们将实现一个线性扫描:对于每个单词,我们计算距离(短路),并列出那些到目前为止达到较小距离的单词.
它对小词典非常有效.
2.改进数据结构
levenshtein距离至少等于长度的差异.
通过使用夫妻(长度,单词)而不仅仅是单词作为键,您可以将搜索限制在长度范围[长度 – 编辑,长度编辑]并大大减少搜索空间.
3.前缀和修剪
为了改进这一点,我们可以说,当我们逐行构建距离矩阵时,一个世界被完全扫描(我们寻找的那个)但另一个(指示物)不是:我们每行只使用一个字母.
这个非常重要的属性意味着对于共享相同初始序列(前缀)的两个指示对象,则矩阵的第一行将是相同的.
请记住,我要求您存储字典排序?这意味着共享相同前缀的单词是相邻的.
假设你正在检查你的单词与卡通和在车上你意识到它不起作用(距离已经太长),那么任何以汽车开头的单词也不起作用,你可以跳过单词,只要他们从汽车开始.
跳过本身可以线性地或通过搜索完成(找到比汽车具有更高前缀的第一个单词):
>如果前缀很长(几个要跳过的单词),线性效果最佳
>二分搜索最适合短前缀(许多单词要跳过)
多长时间取决于你的字典,你必须衡量.我会继续二进制搜索.
注意:长度分区对前缀分区起作用,但它会修剪更多的搜索空间
4.前缀和重复使用
现在,我们还将尝试尽可能多地重复使用计算(而不仅仅是“它不起作用”的结果)
假设你有两个词:
>卡通
>洗车
您首先逐行计算矩阵,用于卡通.然后在阅读洗车时,你需要确定公共前缀的长度(这里是汽车),你可以保留矩阵的前4行(对应于void,c,a,r).
因此,当开始计算洗车时,你实际上开始迭代w.
要做到这一点,只需使用在搜索开始时直接分配的数组,并使其足够大以容纳更大的引用(您应该知道字典中最大的长度).
5.使用“更好”的数据结构
为了更轻松地使用前缀,您可以使用Trie或Patricia树来存储字典.然而,它不是STL数据结构,您需要对其进行扩充以在每个子树中存储存储的字长度范围,因此您必须自己实现.它并不像看起来那么容易,因为存在可能会破坏地方的内存爆炸问题.
这是最后的选择.实施成本很高.