Pemrograman · Algoritme
Penjelasan pencarian string KMP: cara kerja, kode, dan kompleksitas
Jawaban singkat
Algoritma Knuth-Morris-Pratt (KMP) menemukan setiap kemunculan pattern sepanjang m dalam teks sepanjang n dalam waktu O(n + m). Mula-mula KMP membangun prefix function (disebut juga failure table) untuk pattern: untuk setiap posisi, panjang proper prefix terpanjang yang juga merupakan suffix. Saat terjadi ketidakcocokan, KMP memakai tabel ini untuk menggeser pattern tanpa pernah mundur di teks.
Buka Penguji regexMasalah yang diselesaikan KMP
Pencarian string menanyakan di mana sebuah pattern pendek muncul dalam teks yang lebih panjang. Metode naif mencoba setiap posisi awal dan membandingkan karakter satu per satu. Pada teks biasa cara ini cepat, tetapi pada input yang repetitif lambat: mencari aaaab di dalam aaaaaaaaaa membandingkan hampir seluruh pattern di setiap posisi, sehingga waktunya O(n × m).
KMP tidak pernah membaca ulang karakter teks setelah ketidakcocokan. Donald Knuth, James H. Morris, dan Vaughan Pratt menerbitkannya pada 1977 dalam "Fast Pattern Matching in Strings".
Ide dasar dengan kata-kata sederhana
Misalkan Anda mencari pattern ABABC, sudah cocok ABAB, dan karakter teks berikutnya bukan C. Bagian yang baru cocok, ABAB, berakhir dengan AB, yang juga merupakan awal pattern. Jadi Anda sudah tahu percobaan berikutnya dimulai dengan 2 karakter yang cocok, dan Anda dapat melanjutkan perbandingan dari posisi pattern 2 alih-alih mulai dari awal.
Prefix function menyimpan angka-angka ini. pi[i] adalah panjang proper prefix terpanjang dari pattern[0..i] yang juga merupakan suffix-nya (proper berarti bukan seluruh string).
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| pattern[i] | A | B | A | B | A | C | A |
| pi[i] | 0 | 0 | 1 | 2 | 3 | 0 | 1 |
Misalnya, pi[4] = 3 karena ABABA diawali dan diakhiri dengan ABA. pi[5] = 0 karena tidak ada prefix dari ABABAC yang berakhir dengan C.
Contoh langkah demi langkah
Cari pattern ABABC (prefix function 0, 0, 1, 2, 0) dalam teks ABABABC. Variabel k menghitung berapa karakter pattern yang sedang cocok.
| Posisi teks | Karakter teks | k sebelum | Aksi | k sesudah |
|---|---|---|---|---|
| 0–3 | A B A B | 0 | Empat kecocokan berturut-turut | 4 |
| 4 | A | 4 | Tidak cocok dengan pattern[4] = C: set k = pi[3] = 2, lalu A cocok dengan pattern[2] | 3 |
| 5 | B | 3 | Cocok dengan pattern[3] | 4 |
| 6 | C | 4 | Cocok dengan pattern[4]: kecocokan penuh berakhir di 6, sehingga dimulai di 6 − 5 + 1 = 2 | 5 → pi[4] = 0 |
Penunjuk teks maju 7 kali dan tidak pernah mundur. Metode naif akan mengulang dari posisi 1 dan membandingkan sebagian karakter lagi.
Implementasi Python
def prefix_function(p):
pi = [0] * len(p)
k = 0
for i in range(1, len(p)):
while k > 0 and p[i] != p[k]:
k = pi[k - 1] # fall back to a shorter border
if p[i] == p[k]:
k += 1
pi[i] = k
return pi
def kmp_search(text, pattern):
"""Return the start index of every occurrence (overlaps included)."""
if not pattern:
return []
pi, k, hits = prefix_function(pattern), 0, []
for i, ch in enumerate(text):
while k > 0 and ch != pattern[k]:
k = pi[k - 1]
if ch == pattern[k]:
k += 1
if k == len(pattern):
hits.append(i - k + 1)
k = pi[k - 1] # keep going to find overlapping matches
return hits
print(prefix_function("ABABACA")) # [0, 0, 1, 2, 3, 0, 1]
print(kmp_search("ABABABC", "ABABC")) # [2]
print(kmp_search("AAAAA", "AA")) # [0, 1, 2, 3]Kompleksitas waktu dan ruang
| Fase | Waktu | Ruang | Alasan |
|---|---|---|---|
| Membangun prefix function | O(m) | O(m) | k bertambah paling banyak sekali per langkah dan setiap fallback menguranginya, sehingga total fallback paling banyak m. |
| Pencarian | O(n) | O(1) tambahan | Argumen yang sama pada teks: paling banyak n penambahan, sehingga paling banyak n fallback. |
| Total | O(n + m) | O(m) | Kasus terburuk sama dengan kasus terbaik hingga suatu konstanta; tidak ada input yang buruk. |
Algoritma naif O(n × m) pada kasus terburuk tetapi sering mendekati O(n) pada teks bahasa alami, sehingga keunggulan KMP terutama terlihat pada data yang repetitif.
Varian dan kesalahan umum
- Off-by-one pada fallback: setelah ketidakcocokan, set
k = pi[k - 1], bukanpi[k].pidiindeks dengan posisi cocok terakhir. - Memakai
ifalih-alihwhileuntuk fallback: Anda mungkin perlu beberapa fallback berturut-turut sebelum sebuah karakter cocok. - Lupa mereset k setelah kecocokan penuh: set
k = pi[k - 1]untuk menemukan kecocokan yang tumpang tindih, atauk = 0untuk yang tidak tumpang tindih. - Trik penyambungan: hitung prefix function dari
pattern + separator + text, dengan separator yang tidak muncul di keduanya; setiap posisi yang nilainya sama dengan m adalah akhir sebuah kecocokan. - Z-algorithm menghitung array yang sangat mirip dan juga mencari dalam O(n + m).
Kapan memakainya dan penggunaannya di dunia nyata
| Algoritma | Penggunaan terbaik |
|---|---|
| KMP | Waktu linear terjamin untuk satu pattern; teks streaming, karena tidak pernah melihat ke belakang. |
| Boyer-Moore dan variannya | Pattern panjang pada alfabet besar; sering melewati banyak karakter, dipakai di alat seperti grep. |
| Rabin-Karp | Banyak pattern dengan panjang sama, atau deteksi plagiarisme dengan rolling hash. |
| Aho-Corasick | Banyak pattern sekaligus; menggeneralisasi failure link KMP ke trie. |
| Suffix array atau suffix automaton | Banyak kueri berbeda pada teks tetap yang sama. |
- Soal wawancara: implementasi
strStr(), shortest palindrome (prefix function daris + "#" + reversed(s)), repeated substring pattern, periode terkecil string (m - pi[m - 1]). - Dunia nyata: pencarian pada sekuens DNA dan aliran paket jaringan, ketika waktu linear pada kasus terburuk penting.
FAQ
Apa itu prefix function atau failure table pada KMP?
Untuk setiap posisi i pada pattern, tabel ini menyimpan panjang proper prefix terpanjang dari pattern[0..i] yang juga merupakan suffix-nya. Saat terjadi ketidakcocokan, KMP memakainya untuk mengetahui berapa karakter yang masih cocok.
Berapa kompleksitas waktu KMP?
O(n + m): O(m) untuk membangun prefix function dan O(n) untuk memindai teks. Linear bahkan pada kasus terburuk.
Apakah KMP lebih cepat daripada str.find di Python?
Tidak. str.find diimplementasikan dalam C dengan algoritma pencarian yang dioptimalkan dan jauh lebih cepat dalam praktik. KMP berharga karena waktu linear yang terjamin, untuk belajar, dan untuk sekuens non-string.
Apakah KMP menemukan kecocokan yang tumpang tindih?
Ya, jika setelah kecocokan penuh Anda set k = pi[k - 1] alih-alih 0. Misalnya, ia menemukan AA pada posisi 0, 1, 2, dan 3 dalam AAAAA.