Pertanyaan PHP Rutin Tercepat Untuk Mencocokkan Kata


Apa cara tercepat di PHP untuk mengambil daftar kata kunci dan mencocokkannya dengan hasil pencarian (seperti array judul) semua kata?

Misalnya, jika frasa kata kunci saya adalah "sepatu kulit yang bagus", maka judul-judul berikut akan menjadi pertandingan...

  • Dapatkan Beberapa Benar Sepatu Kulit Besar
  • Sepatu kulit Adalah Besar
  • Besar Hari! Itu Ada yang Keren Sepatu kulit!
  • Sepatu, Terbuat dari Kulit, Dapat Besar

... sementara ini tidak akan pertandingan:

  • Sepatu kulit Dijual Hari Ini!
  • Anda akan menyukai ini Sepatu kulit Sangat
  • Sepatu Hebat Jangan Datang Murah

Saya membayangkan ada beberapa trik dengan fungsi array atau RegEx (Regular Expression) untuk mencapai ini dengan cepat.


5
2018-04-13 20:25


asal


Jawaban:


Saya akan menggunakan indeks untuk kata-kata dalam judul dan menguji jika setiap istilah pencarian dalam indeks itu:

$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";
}

4
2018-04-13 22:21



Anda dapat preg_grep () array Anda terhadap sesuatu seperti

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

atau (mungkin lebih cepat) grep setiap kata secara terpisah dan kemudian array_intersect hasilnya


3
2018-04-13 20:55



Ini mungkin solusi yang sangat naif (mungkin ada solusi yang lebih efisien / elegan), tetapi saya mungkin melakukan sesuatu seperti berikut:

$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 );

Tidak tahu bagaimana tolok ukur ini.


2
2018-04-13 21:03



Anda bisa menggunakannya

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

Perhatikan beberapa hal

a) Anda membutuhkan batas kata di kedua ujungnya Anda dapat mengakhiri kata-kata yang cocok yang berisi kata-kata yang Anda cari misalnya "sepatu dari kulit membawa kebesaran".

b) Saya menggunakan pencocokan lazy wildcard (yaitu. *?). Ini meningkatkan efisiensi, karena secara default * adalah tamak (yaitu mengkonsumsi banyak karakter yang dapat dicocokkan, dan hanya memberi mereka mendukung pertandingan secara keseluruhan). Jadi jika kita tidak memiliki trailing?,. * Akan cocok dengan semua yang ada di garis dan kemudian mundur untuk mencocokkan 'hebat'. Prosedur yang sama kemudian diulangi untuk 'sepatu' dan 'kulit'. Dengan membuat * malas, kita menghindari backtracks yang tidak perlu ini.


1
2018-04-13 23:44



Saya tidak tahu tentang itu mutlak cara tercepat, tetapi ini mungkin cara tercepat untuk melakukannya dengan regex:

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

Ini cocok dengan setiap kata dalam string, dan jika kata itu terjadi menjadi salah satu kata kunci Anda, kelompok penangkap kosong "memeriksanya". Setelah semua kata dalam string telah dicocokkan, referensi belakang (\1\2\3) memastikan bahwa masing-masing dari tiga kata kunci telah terlihat setidaknya sekali.

Pendekatan berbasis lookahead yang biasanya direkomendasikan untuk tugas semacam ini perlu memindai berpotensi seluruh string beberapa kali - sekali untuk setiap kata kunci. Regex ini hanya harus memindai string sekali - pada kenyataannya, backtracking dinonaktifkan oleh quantive possifive (++, *+) dan kelompok atom ((?>...)).

Yang mengatakan, saya masih akan pergi dengan pendekatan lookahead kecuali saya tahu itu menyebabkan kemacetan. Dalam sebagian besar kasus, pembacaannya yang lebih besar layak mendapat trade-off dalam kinerja.


1
2018-04-14 01:54



Saya tidak bisa memberikan jawaban yang pasti tetapi saya akan mencoba membandingkan setiap solusi yang disarankan dan akan mulai dengan merantai beberapa in_arraybersama-sama.

if (in_array('great', $list) && in_array('leather', $list) && in_array('shoes', $list)) {
    // Do something
}

1
2018-04-13 20:35