Google就像“你的意思是?”使用Levenshtein距离

时间:2017-08-19 14:43:08

标签: php laravel

我正在构建一个简单的谷歌“你的意思是什么?”使用Levenshtein距离算法搜索建议。我已经设法让它工作,但我有返回结果的问题。

例如,如果关键字是“如何使用 fasebuk twitster ?”我想只替换不正确的单词,以便建议结果为“如何使用 facebook twitter ?”

如何实现这种建议?

以下是我到目前为止所做的事情:

public function searchsuggestion(Request $request)
    {
        $allwords  = array('facebook','orkut','twitter','yahoo', 'microsoft','paypal','google','bing','msn');
        $shortest = -1;
        $keywords = trim($request->input('q'));
        if (! empty($keywords)) {   
            foreach ($allwords as $word) {
                $lev = $this->LevenshteinDistance($keywords, $word);
                if ($lev == 0) {
                    $closest = $word;
                    $shortest = 0;
                    break;
                }
                if ($lev <= $shortest || $shortest < 0) {
                    $closest  = $word;
                    $shortest = $lev;
                }
            }
            if ($shortest == 0) {
                $result = '';
            } else {
                $result = $closest;
            }
            return view('posts/search', compact('result'));
        }
        return redirect()->route('latestposts');
    }

我是新手开发者,任何帮助表示赞赏。谢谢!

1 个答案:

答案 0 :(得分:2)

这是一个很好的问题。为此,您需要单独处理输入字符串中的每个单词。这样,您可以使用字典中的单词替换输入字符串中的单个单词。

根据您的字典,您可能需要对每次点击进行进一步处理以防止误报。当我开始尝试您的代码时,仅使用Levenshtein距离时,输入中的大多数单词都被替换为“msn”。

我整理了一个测试用例,您可以从命令行运行以测试例程。除了单独处理每个单词以便我们可以进行替换之外,我将获得原始单词和词典单词的metaphone个键,然后使用similar_text来获得两者之间的相似性百分比。有一些阈值变量允许您设置Levenshtein距离的上限阈值,设置元电话密钥的长度限制,并设置元电话密钥相似度的最小百分比。

<?php
class Request
{
    private $_data = [];

    public function __construct(array $data = [])
    {
        $this->_data = $data;
    }

    public function input($key, $default = null)
    {
        return (array_key_exists($key, $this->_data)) ? $this->_data[$key] : $default;
    }
}

function searchsuggestion(Request $request)
{
    $allwords = array('facebook', 'orkut', 'twitter', 'yahoo', 'microsoft', 'paypal', 'google', 'bing', 'msn');
    $keywords = trim($request->input('q'));

    $levThreshold       = 3;
    $metaphoneLength    = 5;
    $metaphoneThreshold = 60;

    if (!empty($keywords))
    {
        $keywordArr = explode(' ', $keywords);
        $matchArr   = [];

        foreach ($keywordArr as $currKeyword)
        {
            $shortest = -1;
            $closest  = null;
            foreach ($allwords as $word)
            {
                $lev = levenshtein($currKeyword, $word);

                if ($lev == 0)
                {
                    break;
                }

                if ($lev <= $shortest || $shortest < 0)
                {
                    $closest  = $word;
                    $shortest = $lev;
                }
            }

            if (!empty($closest) && $shortest <= $levThreshold)
            {
                $origSoundex  = metaphone($currKeyword, $metaphoneLength);
                $foundSoundex = metaphone($closest, $metaphoneLength);

                similar_text($origSoundex, $foundSoundex, $percentage);

                if ($percentage > $metaphoneThreshold)
                {
                    $matchArr[$currKeyword] = $closest;
                }
            }
        }

        if (!empty($matchArr))
        {
            $origWords    = array_keys($matchArr);
            $replaceWords = array_values($matchArr);

            return str_replace($origWords, $replaceWords, $keywords);
        }

        return null;
        //return view('posts/search', compact('result'));
    }
    //return redirect()->route('latestposts');
}

$request = new Request(['q' => 'How to use fasebuk and twitster?']);

$response = searchsuggestion($request);

print $response . "\n";