Matn ichida qidiruv uchun yana bir diqqatga molik algoritmlardan biri – Rabin-Karp algoritmi hashing usuliga asoslanadi. Uning ishlash tartibini batafsil tushuntirishga harakat qilaman.
Bizda quyidagi matn bor bo’lsin: FINDINAHAYSTACKNEEDLEINA
Pattern: NEEDLE
- patterndagi 0 dan M-1 gacha belgilar bo’yicha hash soni generatsiya qilinadi. Bunda M – patterndagi belgilar soni.
- har bir [i..N-1] sikl ichida matndagi i dan M + i – 1 gacha belgilar bo’yicha hash soni generatsiya qilinadi. Bunda N – matndagi belgilar soni
- Agar pattern hash va matn substring hash bir-biriga teng bo’lsa, pattern matn ichidan topilgan hisoblanadi.
Hash sonini generatsiya qilishning eng tezkor, oson usullardan biri – pattern’ning har bir belgisi kodini olib, ularni birlashtirish va uni biror katta tub songa bo’lib, qoldig’ini qoldirish kerak. Javascriptda bu ish quyidagicha bajariladi:
const primeNumber = 997 // tub son
const pattern = "NEEDLE" // qidiriladigan so'z
const hash = pattern.split('').map(char => char.charCodeAt()).join('') % primeNumber
// pattern uchun hash soni 769
Demak bizda patternning hash soni generatsiya qilindi. Endi uni matndagi har bir i dan M + i – 1 gacha substring’lardan hash son generatsiya qilib uni pattern’ning hash soniga solishtirib chiqiladi. Substring uchun ham, pattern uchun ham bir xil usulda hash generatsiya qilinadi.
FINDINAHAYSTACKNEEDLEINA
FINDIN // 5 != 769
INDINA // 877 != 769
NDINAH // 105 != 769
DINAHA // 259 != 769
INAHAY // 541 != 769
NAHAYS // 414 != 769
AHAYST // 271 != 769
HAYSTA // 963 != 769
AYSTAC // 805 != 769
YSTACK // 535 != 769
STACKN // 507 != 769
TACKNE // 178 != 769
ACKNEE // 98 != 769
CKNEED // 615 != 769
KNEEDL // 317 != 769
NEEDLE // 769 == 769
Juda oddiy va mohirona usul!
Yuqoridagi hash generatsiya qilish usuli split() va join() metodlari ishlatilgani sababli sekinroq ishlashi mumkin. Shuning uchun biz hash generatsiya qilishni sinalgan usul – Horner metodi yordamida amalga oshiramiz. Umumiy formula quyidagicha:
xi = ( ti * RM-1 + ti+1 * RM-2 + … + ti+M-1 * R0 ) % Q
Bu yerda:
xi – matndagi [i..M + i – 1] substring uchun hash soni.
ti – str.charCodeAt(i) – matn/pattern belgisining ASCII jadvalidagi raqami.
R – radix soni. ASCII uchun 128 yoki 256.
M – patterndagi belgilar soni.
Q – tub son.
Hash funksiya kodi:
function hornerHash(str, M) {
const R = 128
const Q = 997
let hash = 0
for (let j = 0; j < M; j++) {
hash = (R * hash + str.charCodeAt(j)) % Q
}
return hash
}
Hash funksiyada kolliziya ehtimolligi Q tub sonning qanchalik katta son ekanligiga bog’liq. Masalan agar Q son M * N2 dan katta bo’lsa, kolliziya ehtimoli 1/N ga teng bo’ladi.