Java 实现模糊搜索建议/单词补全

Java 实现模糊搜索建议/单词补全,java,string,algorithm,search,nlp,Java,String,Algorithm,Search,Nlp,我有一张一串短语的清单。因为这是一个相当长的列表,我还有一个文本框,用户可以在其中键入作为搜索栏。到目前为止,搜索栏中不完全包含字母的术语将被过滤掉。然而,我想让它列出一些关于这个词可能是什么的建议 注意:我不是在寻找“你的意思是……”或像或那样的拼写检查算法(尽管第一个链接看起来不错);我想要一个算法,能够为不完整的单词或短语建议最佳匹配;e、 g.单词“bat”应该比单词“car”更符合单词“battery” 使用Google的方法返回以(大致)相同字母开头的几个最常见字符串也是不切实际的,

我有一张一串短语的清单。因为这是一个相当长的列表,我还有一个文本框,用户可以在其中键入作为搜索栏。到目前为止,搜索栏中不完全包含字母的术语将被过滤掉。然而,我想让它列出一些关于这个词可能是什么的建议

注意:我不是在寻找“你的意思是……”或像或那样的拼写检查算法(尽管第一个链接看起来不错);我想要一个算法,能够为不完整的单词或短语建议最佳匹配;e、 g.单词
“bat”
应该比单词
“car”
更符合单词
“battery”

使用Google的方法返回以(大致)相同字母开头的几个最常见字符串也是不切实际的,因为据我所知,列表中的每个元素都与其他元素一样常见

另外,我想用Java(8)实现这一点;然而,其他语言的答案是可以接受的,只要它们不使用Java没有等价物的内置函数。如果有用的话,我编写了一个修改版的Levenshtein distance(如下),它用星号填充搜索字符串,表示“任意字符”。这适用于单个单词,例如
“mud”
“muddy”
完美匹配,但考虑到人们可能使用
“car”
搜索
“赛车”

有没有一个好的方法可以为搜索词提供完成建议


提前感谢!

请查看::第77页中的K木瓦方法


它可能会提供一些想法来推动这种模糊搜索系统

总是有简单、暴力的方法。即使有相当大的短语集,它也能很好地工作

假设您有一个包含100万个短语的列表。用户输入字母“c”。您在短语列表中搜索包含字母“c”的所有短语,并显示它们。您还保留该结果

用户然后键入“a”。现在,您可以在上一次搜索返回的字符串列表中搜索字符串“ca”。因此,您已经将搜索范围从所有短语中剪切为仅包含字母“c”的短语。考虑到大约37%的英语单词包含字母“c”(请参阅),您已经将您的列表减少了近三分之二

无论如何,您现在有了一个包含字母“ca”的短语列表。与所有短语列表相比,此列表将非常小。您可以在用户键入字符时继续优化列表

如果整个列表的初始搜索时间过长,您可以通过创建字典、按字母索引并拥有包含该字母的单词列表来轻松优化。例如,“c”的条目将包含“赛车”、“汽车”、“猫”、“卡弗大师”等。因此,无需进行搜索即可获得初始列表

使用字典方法的另一个好处是,可以对每个字母的列表进行预处理,使以字母开头的单词位于列表的最前面。这很好,因为大多数时候,当有人在搜索时,他都在寻找以他键入的第一个字母开头的单词或短语。但是你可以很容易地找到受欢迎程度或任何其他标准的影响

我已经多次使用这种方法,而且效果很好。它非常容易实现,通常执行速度足够快,不需要任何优化。我上面提到的字典优化对于除少数几种简单暴力方法不起作用的情况外的所有情况都是足够的,有一次我需要两个字典s:第一个字符一个,字母对一个


即使这不是最终的解决方案,它也很有用,因为它很容易证明正确,并测试其他更复杂的算法。

是的;这样的方法会很好地工作。然而,虽然“ca”会给出“汽车”、“猫”和“赛车”,但它也会给出诸如“因为”或“电气”之类的东西,“这是不可能完成的。这可能是最终解决方案的一部分,正如您所说,这是一个很好的测试指标;“这是一个相当长的列表”只是指作为一个用户浏览它会很乏味,特别是当用户不知道他/她正在寻找的条目的确切名称时;大概只有大约200个条目长。@ricky3350:如果列表相当小(200个非常小),您可以进行大量预处理,以确保相关内容显示在列表的顶部。例如,在“ca”的情况下,您可以手工构造项目的显示顺序,以便“car”、“cat”和“race car”在“because”和“electric”之前显示。尝试一下,看起来相当不错;然而,它仍然是一种比较的方法,不是完备性的,也是针对文档的,而不是小短语。仍然可以是好的;谢谢
/**
 * <ul>
 * <b><i>searchDistance</i></b><br>
 * <br>
 * <code>&nbsp;public static int searchDistance(String key, String match)</code><br>
 * <br>
 * Gets the Levenshtein distance between <code>key</code> and <code>match</code>. <br>
 * If <code>useAsterisk</code> is true, then the follwing applies: If <code>key</code> is shorter than <code>match</code>, the asterisk <code>'*'</code> is appended to it until the lengths are equal. Asterisks can be used in <code>key</code> to signify 'any character.'
 * @param key - The text to search for
 * @param match - The text to compare <code>key</code> against
 * @param useAsterisk - Whether or not to use asterisks for the purpose described above
 * @return the Levenshtein distance between <code>key</code> and <code>match</code>.
 *         </ul>
 */
public static int searchDistance(String key, String match, boolean useAsterisk) {
    while (key.length() < match.length()) {
        key = key + "*";
    }

    int[][] matrix = new int[key.length() + 1][match.length() + 1];

    for (int i = 0; i < matrix.length; i++) {
        matrix[i][0] = i;
    }

    for (int i = 0; i < matrix[0].length; i++) {
        matrix[0][i] = i;
    }

    for (int a = 1; a < matrix.length; a++) {
        for (int b = 1; b < matrix[0].length; b++) {
            matrix[a][b] = Math.min(Math.min(matrix[a - 1][b] + 1, matrix[a][b - 1] + 1), matrix[a - 1][b - 1] + (key.charAt(a - 1) == match.charAt(b - 1) || key.charAt(a - 1) == '*' ? 0 : 1));
        }
    }

    return matrix[matrix.length - 1][matrix[0].length - 1];
}