【问题标题】:Rearrange words using Levenshtein distance使用 Levenshtein 距离重新排列单词
【发布时间】:2019-01-19 13:09:24
【问题描述】:

总结

我正在尝试在 php 中查找名称匹配百分比,但在此之前我需要根据第一个字符串重新排列字符串中的单词。

源代码是关于什么的?

我有两个字符串。首先,如果在字符串中找到空间,我将两个字符串都添加到数组中,将其添加到数组中。 $arraydataBaseName 和 $arraybankData 来自我的第一个数组,即 $arraydataBaseName 我正在搜索 $arraybankData 的所有值并获取密钥。我得到了正确的键排列,但无法将它们特定位置的值排列到新数组中。

$dataBaseName = "Jardine Lloyd Thompson";
$bankdata = "Thompson Thompson Jardine"; 

$replacedataBaseName = preg_replace("#[\s]+#", " ", $dataBaseName);
$replacebankData = preg_replace("#[\s]+#", " ", $bankdata); 

$arraydataBaseName = explode(" ",$replacedataBaseName);
$arraybankData = explode(" ",$replacebankData); 

echo "<br/>";
print_r($arraydataBaseName);

$a="";
$i="";
$arraysize =  count($arraydataBaseName);

$push=array();
for($i=0;$i< $arraysize;$i++)
{     
  if(array_search($arraybankData[$i],$arraydataBaseName)>0)
  {
    ${"$a$i"} =  array_search($arraybankData[$i],$arraydataBaseName); 
    //echo ${"$a$i"};
    array_push($push,${"$a$i"});
   }    
 }
 print_r($push); 

案例一:

输入

数据库名称 = Jardine Lloyd Thompson

银行名称 = Thompson Jardine Lloyd

输出

预期输出 = Jardine Lloyd Thompson

案例2:##

输入

数据库名称 = Jardine Lloyd Thompson

银行名称 = Thoapson Jordine Llayd

如果在上面的 DatabaseName 中没有找到单词,那么预期的搜索将基于 Leventish 算法单词,该单词的距离较小,将被视为键

输出

预期输出 = Jordine Llayd Thoapson

Description of Problem

问题更新

当用户输入 $bankdata 包含更多无法匹配的单词时,我需要将它们附加到末尾。

【问题讨论】:

  • @Quasimodo'sclone 我已经成功通过比较 $bankdata 和 $dataBaseName 来获得单词的位置
  • 很好的作业!是否涉及真正的数据库?
  • @Quasimodo'sclone 不只是变量名
  • @Quasimodo'sclone 是的,我能够轻松地找出距离。排列单词是我卡住的一步

标签: php fuzzy-logic fuzzywuzzy


【解决方案1】:

这是一个简单的版本,随后逐字查找最佳匹配。

declare (strict_types=1);

$dataBaseName = 'Jardine Lloyd Thompson';

$bankdataRows =
[
  'Thompson Jardine Lloyd',
  'Blaaa  Llayd Thoapson   f***ing user input   Jordine   aso. ',
];

// assume the "database" is already stored trimmed since it is server-side controlled
$dbWords = preg_split("#[\s]+#", $dataBaseName);

foreach ($bankdataRows as $bankdata)
{
  // here we trim the data received from client-side.
  $bankWords = preg_split("#[\s]+#", trim($bankdata));
  $result    = [];

  if(!empty($bankWords))
    foreach ($dbWords as $dbWord)
    {
      $idx   = null;
      $least = PHP_INT_MAX;

      foreach ($bankWords as $k => $bankWord)
        if (($lv = levenshtein($bankWord, $dbWord)) < $least)
        {
          $least = $lv;
          $idx   = $k;
        }

      $result[] = $bankWords[$idx];
      unset($bankWords[$idx]);
    }

  $result = array_merge($result, $bankWords);
  var_dump($result);
}

结果

array(3) {
  [0] =>
  string(7) "Jardine"
  [1] =>
  string(5) "Lloyd"
  [2] =>
  string(8) "Thompson"
}

array(8) {
  [0] =>
  string(7) "Jordine"
  [1] =>
  string(5) "Llayd"
  [2] =>
  string(8) "Thoapson"
  [3] =>
  string(5) "Blaaa"
  [4] =>
  string(7) "f***ing"
  [5] =>
  string(4) "user"
  [6] =>
  string(5) "input"
  [7] =>
  string(4) "aso."
}

See live fiddle

您可能希望扩展此方法,首先计算每个可能组合的 Levenshtein 距离,然后选择最佳的整个匹配。

【讨论】:

  • 感谢您的分享 我与您的解决方案太接近了,但是当我通过 $dataBaseName = trim('jardine Marks llord thompson'); $bankdataRows =[$dataBaseName,trim('lloyd thodal jardine')];我得到的输出是正确的,但有错误 Notice: Undefined offset: 0 在这种情况下 $dataBaseName = trim('jardine llord thompson'); $bankdataRows =[$dataBaseName,trim('lloyd thodal jardine spark')];当第二个变量有 4 个单词时,如果 1 个单词不匹配,则应将其附加在末尾或空白处,请建议我尝试过但遇到一些问题
  • @daoootim 只需附加剩余的银行字词$result = array_merge($result, $bankWords); 为了保持剩余的顺序,我已将排序转换为foreach 循环。
  • @daoootim 如果有更多问题之前未在问题中描述,请根据 SO 政策针对一个特定问题打开一个新问题。
  • @卡西莫多的克隆传奇谢谢你是冠军
  • 当我尝试这个名称时需要你的帮助 DatabaseName ='E SRINIVAS' 和 BankName ='SRINIVAS ETTAMALLA' ExpectedOutput = 'ETTAMALLA SRINIVAS' 我得到这个输出 = 'SRINIVAS ETTAMALLA'
【解决方案2】:

我已经分解了案例 1 和 2 中的代码。
但很明显,如果 var_export 为 false,您将使用相同的变量执行案例 2 代码。

//Case 1:
$DatabaseName = "Jardine Lloyd Thompson";
$BankName = "Thompson Jardine Lloyd";

//Split and sort them
$data = explode(" ", $DatabaseName);
$bank = explode(" ", $BankName);
sort($data);
sort($bank);
Var_export(($data == $bank)); //true

//Case 2
$DatabaseName = "Jardine Lloyd Thompson";
$BankName = "Thoapson Jordine Llayd";

//Split and sort
$data = explode(" ", $DatabaseName);
$bank = explode(" ", $BankName);
sort($data);
sort($bank);

// Loop and accumulate the levenshtein return
$lev = 0;
foreach($data as $key => $name){
    $lev += levenshtein($name, $bank[$key]);
}

echo PHP_EOL . $lev; // 3 letters "off"

https://3v4l.org/eP5PE

案例 1 和 2 在同一代码中的示例。

$DatabaseName = "Jardine Lloyd Thompson";
$BankName = "Thoapson Jordine Llayd";

$data = explode(" ", $DatabaseName);
$bank = explode(" ", $BankName);
sort($data);
sort($bank);
if($data == $bank){
    echo "true";
    exit;
    // No need to do levenshtein
}

$lev = 0;
foreach($data as $key => $name){
    $lev += levenshtein($name, $bank[$key]);
}

echo PHP_EOL . $lev;

https://3v4l.org/RJSiB

【讨论】:

  • @Andreas 感谢您的回答,但在找到 levenshtein 后需要重新排列单词 例如:DatabaseName ='E SRINIVAS' 和 BankName ='SRINIVAS ETTAMALLA' ExpectedOutput = 'ETTAMALLA SRINIVAS'