Pencarian pola merupakan masalah penting dalam ilmu komputer. Saat kita melakukan
pencarian string di file notepad/word, browser, atau database, algoritma pencarian pola
digunakan untuk menampilkan hasil pencarian. Pernyataan masalah yang khas adalah-
Diberikan teks txt[0..n-1] dan pola pat[0..m-1] di mana n adalah panjang teks dan m adalah
panjang pola, tuliskan fungsi search(char pat[] , char txt[]) yang mencetak semua
kemunculan pat[] di txt[]. Anda mungkin berasumsi bahwa n > m.
Contoh:
Pada postingan kali ini kita akan membahas algoritma pencarian pola Boyer Moore.
Seperti algoritma KMP dan Finite Automata, algoritma Boyer Moore juga melakukan
praproses terhadap pola tersebut. Boyer Moore merupakan kombinasi dari dua pendekatan
berikut:
1. Heuristik Karakter Buruk
2. Heuristik Sufiks yang Baik
Kedua heuristik di atas juga dapat digunakan secara mandiri untuk mencari suatu pola dalam
sebuah teks. Pertama-tama mari kita pahami bagaimana dua pendekatan independen
bekerja sama dalam algoritma Boyer Moore. Jika kita melihat pada algoritma Naive,
algoritma ini menggeser pola ke atas teks satu per satu. Algoritma KMP melakukan
preprocessing terhadap pola tersebut sehingga pola tersebut dapat digeser lebih dari satu.
Algoritma Boyer Moore melakukan prapemrosesan untuk alasan yang sama. Ini memproses
pola dan membuat array berbeda untuk masing-masing dari dua heuristik. Pada setiap
langkah, ia menggeser pola sebanyak maksimal slide yang disarankan oleh masing-masing
dua heuristik. Jadi ia menggunakan offset terbesar yang disarankan oleh dua heuristik pada
setiap langkah.
Berbeda dengan algoritma pencarian pola sebelumnya, algoritma Boyer Moore
memulai pencocokan dari karakter terakhir dari pola tersebut. Pada postingan kali ini kita
akan membahas tentang heuristik karakter buruk dan heuristik Sufiks Baik pada postingan
selanjutnya.
Penjelasan: Pada contoh di atas, kita mendapatkan ketidakcocokan pada posisi 3. Di sini
karakter ketidakcocokan kita adalah “A”. Sekarang kita akan mencari kemunculan terakhir
dari pola “A”. Kami mendapat “A” di posisi 1 dalam pola (ditampilkan dalam warna Biru) dan
ini adalah kemunculan terakhirnya. Sekarang kita akan menggeser pola sebanyak 2 kali
sehingga pola “A” sejajar dengan teks “A”
Kasus 2 – Pola bergerak melewati karakter yang tidak cocok
Kami akan mencari posisi kemunculan terakhir karakter yang tidak cocok dalam pola dan jika
karakter tidak ada, kami akan menggeser pola melewati karakter yang tidak cocok tersebut.
Penjelasan:
Di sini kita memiliki ketidakcocokan di posisi 7. Karakter "C" yang tidak cocok tidak ada
dalam pola sebelum posisi 7 jadi kita akan menggeser pola melewati posisi 7 dan akhirnya
dalam contoh di atas kita mendapatkan pola yang sangat cocok (ditampilkan dalam Hijau).
Hal ini kita lakukan karena “C” tidak ada pada polanya sehingga pada setiap shift sebelum
posisi 7 akan terjadi ketidakcocokan dan pencarian kita tidak membuahkan hasil.
Dalam implementasi berikut, kami melakukan praproses pola dan menyimpan kemunculan
terakhir dari setiap karakter yang mungkin dalam array dengan ukuran yang sama dengan
ukuran alfabet. Jika karakter tidak ada sama sekali, maka dapat mengakibatkan pergeseran
sebesar m (panjang pola). Oleh karena itu, heuristik karakter buruk membutuhkan waktu
O(n/m) dalam kasus terbaik.
int badchar[NO_OF_CHARS];
else
/* Shift the pattern so that the bad character
in text aligns with the last occurrence of
it in pattern. The max function is used to
make sure that we get a positive shift.
We may get a negative shift if the last
occurrence of bad character in pattern
is on the right side of the current
character. */
s += max(1, j - badchar[txt[s + j]]);
}
}
/* Driver code */
int main()
{
string txt= "ABAAABCD";
string pat = "ABC";
search(txt, pat);
return 0;
}
Output
pattern occurs at shift = 4
Kompleksitas Waktu : O(n x m)
Ruang Tambahan: O(1)
Heuristik Karakter Buruk mungkin memerlukan waktu O(mn) dalam kasus terburuk.
Kasus terburuk terjadi ketika semua karakter teks dan polanya sama. Misalnya, txt[] =
“AAAAAAAAAAAAAAAAAA” dan pat[] = “AAAAA”. Heuristik Karakter Buruk mungkin
memerlukan O(n/m) dalam kasus terbaik. Kasus terbaik terjadi ketika semua karakter teks
dan pola berbeda.
https://www.geeksforgeeks.org/boyer-moore-algorithm-for-pattern-searching/
2. Heuristik Sufiks yang Baik
Heuristik untuk pencarian pola. Sama seperti heuristik karakter buruk, tabel
prapemrosesan dibuat untuk heuristik sufiks yang baik.
Heuristik Sufiks yang Baik
Membiarkan
T
menjadi substring teks
T
yang cocok dengan substring pola
P
Sekarang kita menggeser polanya sampai : 1) Kemunculan t lagi di P yang cocok
dengan t di T. 2) Awalan P, yang cocok dengan akhiran t 3) P bergerak melewati t
Penjelasan:
Pada contoh di atas, kita mendapatkan substring t dari teks T yang cocok dengan pola
P (berwarna hijau) sebelum ketidakcocokan pada indeks 2. Sekarang kita akan mencari
kemunculan t (“AB”) di P. Kita telah menemukan kemunculan yang dimulai pada posisi 1
(berlatar belakang kuning) jadi kita geser polanya ke kanan sebanyak 2 kali untuk
menyelaraskan t di P dengan t di T. Ini adalah aturan lemah dari Boyer Moore asli dan tidak
terlalu efektif, kita akan membahas aturan Sufiks Baik yang Kuat.
Penjelasan:
Dalam contoh di atas, kita mendapatkan t (“BAB”) yang cocok dengan P (berwarna
hijau) pada indeks 2-4 sebelum mismatch. Tetapi karena tidak ada kemunculan t di P, kita
akan mencari beberapa awalan P yang cocok dengan beberapa akhiran t. Kami telah
menemukan awalan “AB” (dengan latar belakang kuning) dimulai dari indeks 0 yang tidak
cocok dengan keseluruhan t tetapi akhiran t “AB” yang dimulai dari indeks 3. Jadi sekarang
kita akan menggeser pola sebanyak 3 kali untuk menyelaraskan awalan dengan akhiran .
Penjelasan:
Dalam contoh di atas,
q = P[7 hingga 8]
dicocokkan dengan t di T. Karakter yang tidak cocok C adalah “C” pada posisi P[6]. Sekarang
jika kita mulai mencari t di P kita akan mendapatkan kemunculan t pertama yang dimulai
pada posisi 4. Namun kemunculan ini didahului oleh “C” yang sama dengan c, jadi kita lewati
ini dan lanjutkan pencarian. Pada posisi 1 kita mendapat kemunculan t lainnya (dengan latar
belakang kuning). Kejadian ini diawali dengan “A” (berwarna biru) yang tidak setara dengan
c. Jadi kita akan menggeser pola P sebanyak 6 kali untuk menyelaraskan kejadian ini dengan
t di T. Kita melakukan ini karena kita sudah mengetahui karakter tersebut c = “C”
menyebabkan ketidaksesuaian. Jadi setiap kemunculan t yang diawali dengan c akan kembali
menyebabkan ketidakcocokan jika disejajarkan dengan t, oleh karena itu lebih baik dilewati
saja. Pemrosesan awal untuk heuristik sufiks yang baik. Sebagai bagian dari prapemrosesan,
sebuah array menggeser dibuat. Setiap entri pergeseran[i] mengandung pola jarak akan
bergeser jika terjadi ketidaksesuaian pada posisi i-1. Yaitu sufiks pola yang dimulai dari posisi
Saya cocok dan ketidakcocokan terjadi pada posisinya i-1. Pemrosesan awal dilakukan secara
terpisah untuk sufiks kuat yang baik dan kasus 2 yang dibahas di atas.
1) Pemrosesan awal untuk Sufiks Baik yang Kuat
Sebelum membahas preprocessing, mari kita bahas dulu ide tentang border. A berbatasan
adalah substring yang merupakan sufiks dan awalan yang tepat. Misalnya saja pada string
“ccacc”, "C" adalah perbatasan, “cc” adalah batas karena muncul di kedua ujung string tetapi
“ca” bukan merupakan perbatasan. Sebagai bagian dari pemrosesan awal array bpos (posisi
perbatasan) dihitung. Setiap entri bpos[i] berisi indeks awal batas untuk sufiks yang dimulai
dari indeks i dalam pola tertentu P. Sufiks? dimulai dari posisi m tidak memiliki batas, jadi
bpos[m] diatur ke m+1 Di mana M adalah panjang polanya. Pergeseran posisi tersebut
diperoleh dengan batas yang tidak dapat diperpanjang ke kiri. Berikut adalah kode untuk
prapemrosesan:
Penjelasan:
Pertimbangkan polanya
P = “ABBABAB”, m = 7
Batas terlebar sufiks “AB” yang bermula pada posisi i = 5 adalah ?(tidak ada) yang
bermula pada posisi 7 sehingga bpos[5] = 7.
Pada posisi i = 2 sufiksnya adalah “BABAB”. Batas terlebar sufiks ini adalah “BAB”
dimulai dari posisi 4, jadi j = bpos[2] = 4.
Kami bisa mengerti
bpos[i] = j
menggunakan contoh berikut:
Jika karakter
#
Yang ada pada posisinya
i-1
setara dengan karakter
?
di posisi
j-1
, kita tahu perbatasan itu akan terjadi
? + batas akhiran pada posisi i
yang dimulai dari posisi
J
yang setara dengan mengatakan itu
batas akhiran pada i-1 dimulai pada j-1
atau
bpos[ i-1 ] = j-1
atau dalam kode:
Ini adalah kondisi yang kita diskusikan dalam kasus 2. Ketika karakter sebelum
kemunculan t di pola P berbeda dengan karakter yang tidak cocok di P, kita berhenti
melewatkan kemunculannya dan menggeser polanya. Jadi disini
P[saya] == P[j]
Tetapi
P[i-1] != p[j-1]
jadi kita mengubah pola dari
i ke j
. Jadi
shift[j] = j-i
adalah perekam untuk
J
. Jadi kapan pun terjadi ketidaksesuaian pada posisi
J
kita akan mengubah polanya
pergeseran[j+1]
posisi ke kanan. Dalam kode di atas kondisi berikut ini sangat penting –
while(i>0)
{
/*if character at position i-1 is not equivalent to
character at j-1, then continue searching to right
of the pattern for border */
while(j<=m && pat[i-1] != pat[j-1])
{
/* the character preceding the occurrence of t in
pattern P is different than the mismatching character in P,
we stop skipping the occurrences and shift the pattern
from i to j */
if (shift[j]==0)
shift[j] = j-i;
//do preprocessing
preprocess_strong_suffix(shift, bpos, pat, m);
preprocess_case2(shift, bpos, pat, m);
//Driver
int main()
{
char text[] = "ABAAAABAACD";
char pat[] = "ABA";
search(text, pat);
return 0;
}
Output
https://www.geeksforgeeks.org/boyer-moore-algorithm-good-suffix-heuristic/
Buat dan inisialisasi tabel last occurrence untuk setiap karakter dalam pola
pencarian. Tabel ini memetakan setiap karakter ke indeks terakhir kemunculannya
dalam pola.
2. Pencarian Mundur :
Mulailah pencarian dari ujung pola dan cocokkan karakter pola dengan karakter teks
dari belakang ke depan.
3. Pencarian Utama:
Jika indeks pola mencapai 0, pola ditemukan dalam teks pada posisi tertentu. Tangani
hasil pencarian sesuai kebutuhan, misalnya, cetak indeks atau lakukan Tindakan lain.
5. Optimasi dan penyesuaian Tambahan:
Uji algoritma pada berbagai jenis teks dan pola untuk memastikan keakuratannya
dan kinerjanya. Evaluasi hasil dan lakukan penyesuaian jika diperlukan.
Teks[1+j+1..i+n-1]=patterns[j+1..n-1] Dan a= teks[1+j] tidak sama dengan b= pattern [j]. Jika u adalah
akhiran dari pattern sebelum b dan v adalah sebuah awalan dari pattern, maka penggeseran-penggeseran
yang mungkin adalah:
2. Penggeseran bad-character yang terdiri dari menyejajarkan teks [i+j] dengan kemunculan
paling kanan karakter tersebut di pattern. Bila karakter tersebut tidak ada di pattern, maka
pattern akan disejajarkan dengan teks[i+n+1].
Secara sistematis, langkah-langkah yang dilakukan algoritme Boyer-Moore pada saat
mencocokkan string adalah:
http://www.iti.fh-flensburg.de/lang/algorithmen/pattern/bmen.htm
#include <iostream>
#include <unordered_map>
int boyerMooreSearch(const std::string& text, const std::string& pattern) {
int m = pattern.length();
int n = text.length();
std::unordered_map<char, int> lastOccurrence;
// Inisialisasi tabel last occurrence
for (int i = 0; i < m; ++i) {
lastOccurrence[pattern[i]] = i;
}
int i = m - 1; // Pointer pada pattern
int j = m - 1; // Pointer pada text
while (j < n) {
if (pattern[i] == text[j]) {
if (i == 0) {
return j; // Pattern ditemukan
} else {
--i;
--j;
}
} else {
if (lastOccurrence.find(text[j]) != lastOccurrence.end()) {
j = j + m - std::min(i, 1 + lastOccurrence[text[j]]);
} else {
j = j + m;
}
i = m - 1;
}
}
return -1; // Pattern tidak ditemukan
}
int main() {
std::string text = "aku punya apel dan pisang.";
std::string pattern = "apel";
int result = boyerMooreSearch(text, pattern);
if (result != -1) {
std::cout << "Pattern ditemukan pada indeks: " << result << std::endl;
} else {
std::cout << "Pattern tidak ditemukan." << std::endl;
}
return 0;
}