Самая быстрая процедура PHP для сравнения слов - PullRequest
5 голосов
/ 14 апреля 2010

Какой самый быстрый способ в PHP взять список ключевых слов и сопоставить его с результатом поиска (например, массивом заголовков) для всех слов ?

Например, если мойключевая фраза " большие кожаные ботинки ", тогда следующие названия будут соответствием ...

  • Get Some Really большие кожаные ботинки
  • Кожаные ботинки Отличны Великолепны
  • Великолепны День!Это крутые кожаные туфли !
  • туфли , изготовленные из кожи , могут быть великолепными

... пока эти не будут совпадением:

  • Кожаная обувь уже в продаже сегодня!
  • YouВам понравятся эти Кожаные ботинки Великолепно
  • Отличная обувь Не придете дешево

Я думаю, что есть какой-то трюк с функциями массиваRegEx (регулярное выражение) для быстрого достижения этой цели.

Ответы [ 6 ]

4 голосов
/ 14 апреля 2010

Я бы использовал индекс для слов в заголовках и проверил, есть ли каждый поисковый термин в этом индексе:

$terms = explode(' ', 'great leather shoes');
$titles = array(
    'Get Some Really Great Leather Shoes',
    'Leather Shoes Are Great',
    'Great Day! Those Are Some Cool Leather Shoes!',
    'Shoes, Made of Leather, Can Be Great'
);
foreach ($titles as $title) {
    // extract words in lowercase and use them as key for the word index
    $wordIndex = array_flip(preg_split('/\P{L}+/u', mb_strtolower($title), -1, PREG_SPLIT_NO_EMPTY));
    // look up if every search term is in the index
    foreach ($terms as $term) {
        if (!isset($wordIndex[$term])) {
            // if one is missing, continue with the outer foreach
            continue 2;
        }
    }
    // echo matched title
    echo "match: $title";
}
3 голосов
/ 14 апреля 2010

вы можете preg_grep () ваш массив против чего-то вроде

 /^(?=.*?\bgreat)(?=.*?\bleather)(?=.*?\shoes)/

или (возможно, быстрее) grep каждое слово отдельно, а затем array_intersect результаты

2 голосов
/ 14 апреля 2010

Это может быть довольно наивное решение (вполне возможно, есть более эффективные / элегантные решения), но я, вероятно, сделаю что-то вроде следующего:

$keywords = array(
    'great',
    'leather',
    'shoes'
);

$titles = array(
    'Get Some Really Great Leather Shoes',
    'Leather Shoes Are Great',
    'Great Day! Those Are Some Cool Leather Shoes!',
    'Shoes, Made of Leather, Can Be Great',
    'Leather Shoes on Sale Today!',
    'You\'ll Love These Leather Shoes Greatly',
    'Great Shoes Don\'t Come Cheap'
);

$matches = array();
foreach( $titles as $title )
{
  $wordsInTitle = preg_split( '~\b(\W+\b)?~', $title, null, PREG_SPLIT_NO_EMPTY );
  if( array_uintersect( $keywords, $wordsInTitle, 'strcasecmp' ) == $keywords )
  {
    // we have a match
    $matches[] = $title;
  }
}

var_dump( $matches );

Понятия не имею, как это все же.

1 голос
/ 14 апреля 2010

Я не знаю о абсолютном самом быстром способе, но это, вероятно, самый быстрый способ сделать это с помощью регулярного выражения:

'#(?:\b(?>great\b()|leather\b()|shoes\b()|\w++\b)\W*+)++\1\2\3#i'

Это соответствует каждому слову в строке, и если это слово является одним из ваших ключевых слов, пустая группа захвата "проверяет его". Как только все слова в строке сопоставлены, обратные ссылки (\1\2\3) гарантируют, что каждое из трех ключевых слов было просмотрено хотя бы один раз.

Подход, основанный на прогнозировании, который обычно рекомендуется для такого рода задач, должен сканировать потенциально всю строку несколько раз - по одному разу для каждого ключевого слова. Это регулярное выражение должно сканировать строку только один раз - фактически, обратное отслеживание отключено притяжательными квантификаторами (++, *+) и атомными группами ((?>...)).

Тем не менее, я все равно пойду с осторожным подходом, если не буду знать, что это вызывает узкое место. В большинстве случаев его большая читаемость стоит компромисса в производительности.

1 голос
/ 14 апреля 2010

Вы можете использовать

/(?=.*?\great\b)(?=.*?\bshoes\b)(?=.*?\bleather\b)/

Обратите внимание на пару вещей

a) Вам нужны границы слов на обоих концах, иначе вы можете в конечном итоге найти слова, содержащие слова, которые вы ищете, например, "обувь из кожи приносит величие".

б) Я использую ленивое сопоставление с подстановочными знаками (т. Е. *?). Это повышает эффективность, так как по умолчанию * является жадным (то есть он потребляет столько символов, сколько может соответствовать, и отказывается от них только в пользу общего соответствия). Так что, если у нас нет трейлинга?,. * Будет соответствовать всему в строке и затем возвращаться, чтобы соответствовать «отлично». Затем ту же процедуру повторяют для «обуви» и «кожи». Делая * ленивым, мы избегаем этих ненужных возвратов.

1 голос
/ 14 апреля 2010

Я не могу предложить вам точного ответа, но я бы попробовал сравнить каждое предлагаемое решение и начать с объединения нескольких in_array вместе.

if (in_array('great', $list) && in_array('leather', $list) && in_array('shoes', $list)) {
    // Do something
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...