Anda di halaman 1dari 11

STRING

BAB membahas properti dasar string. Algoritma yang mengeksploitasi properti khusus kunci

string lebih cepat dan lebih fleksibel daripada algoritma yang kami pertimbangkan sebelumnya Di

pertimbangkan algoritma untuk pencarian substring, termasuk algoritma terkenal karena Knuth, Morris,

dan Pratt. kami memperkenalkan ekspresi reguler, dasar dari masalah pencocokan pola, generalisasi

pencarian substring, dan alat pencarian klasik yang dikenal sebagai grep. Algoritma klasik ini didasarkan

pada perangkat konseptual terkait yang dikenal sebagai bahasa formal dan automata terbatas. dikhususkan

untuk kompresi data aplikasi pusat, di mana kami mencoba untuk mengurangi ukuran string sebanyak

mungkin.

1. URUTAN STRING
Menghitungan indeks kunci Sebagai pemanasan, kami mempertimbangkan metode sederhana

untuk menyortir yang efektif setiap kali kunci adalah bilangan bulat kecil. nomor bagian adalah bilangan

bulat kecil, pengurutan berdasarkan penghitungan indeks kunci adalah tepat. Untuk mendeskripsikan

metode, kita asumsikan bahwa informasi disimpan dalam array a[] item yang masing-masing berisi nama

dan nomor bagian, Smith bahwa nomor bagian adalah bilangan bulat antara 0 dan R-1, dan bahwa kode

a[i ]. key() mengembalikan nomor bagian untuk count[] dipecah menjadi empat langkah sebagai berikut:

1. Hitung frekuensi.

Langkah pertama adalah menghitung 1 frekuensi kemunculan setiap nilai kunci, menggunakan 2

sebuah array int count []. Untuk setiap item, kami menggunakan kunci untuk mengakses entri

dalam hitungan [] dan menambah entri itu. Jika nilai kuncinya adalah r, kita menambah hitungan

[r+1]. (Mengapa +1? Alasan untuk itu akan menjadi jelas pada langkah berikutnya.) 2 Pada contoh

di sebelah kiri, kita menghitung kenaikan pertama[3] karena Anderson ada di bagian 2, kemudian

kita menghitung kenaikan [4] dua kali karena Brown dan Davis ada di bagian 3, dan seterusnya.

Perhatikan bahwa hitungan [0] selalu 0, 5 dan hitungan [1] adalah 0 dalam contoh ini (tidak ada

siswa yang 5 di bagian 0).


2. Ubah hitungan menjadi indeks.

Selanjutnya, kita menggunakan count [] for (int r = 0 untuk menghitung, untuk setiap nilai kunci,

indeks awal menghitung posisi [r+1] dalam urutan item yang diurutkan dengan kunci tersebut.

Dalam contoh kita, karena ada tiga item dengan kunci 1 dan lima item dengan kunci 2, kemudian

item dengan kunci 3 dimulai pada posisi 8 dalam larik yang diurutkan.Secara umum, untuk

mendapatkan indeks awal item dengan nilai kunci tertentu, kami menjumlahkan jumlah frekuensi

dari nilai yang lebih kecil .Untuk setiap nilai kunci r, jumlah hitungan untuk nilai kunci kurang

dari r+1 sama dengan jumlah hitungan untuk nilai kunci kurang dari r ditambah hitungan [r],

sehingga mudah untuk melanjutkan dari kiri ke kanan untuk mengubah hitungan [] menjadi tabel

indeks yang dapat kita gunakan untuk mengurutkan data.


3. Distribusikan datanya.

Dengan count[] array diubah menjadi tabel indeks, kami menyelesaikan pengurutan aktual

dengan memindahkan item ke array tambahan aux []. Kami memindahkan setiap item ke posisi di

aux [] yang ditunjukkan oleh entri count [] yang sesuai dengan kuncinya, dan kemudian

menambah entri tersebut untuk mempertahankan invarian berikut untuk count []: untuk setiap nilai

kunci r, count[ r] adalah indeks posisi di aux [] di mana item berikutnya dengan nilai kunci r (jika

ada) harus ditempatkan. Proses ini menghasilkan hasil yang diurutkan dengan satu melewati data,

seperti yang diilustrasikan di sebelah kiri. Catatan: Dalam salah satu aplikasi kami, fakta bahwa

implementasi ini stabil sangatlah penting: item dengan kunci yang sama disatukan tetapi disimpan

dalam urutan yang relatif sama.

4. Distribusikan datanya.

Dengan count[] array diubah menjadi tabel indeks, kami menyelesaikan pengurutan aktual

dengan memindahkan item ke array tambahan aux []. Kami memindahkan setiap item ke posisi di

aux [] yang ditunjukkan oleh entri count [] yang sesuai dengan kuncinya, dan kemudian

menambah entri tersebut untuk mempertahankan invarian berikut untuk count []: untuk setiap nilai

kunci r, count[ r] adalah indeks posisi di aux [] di mana item berikutnya dengan nilai kunci r (jika

ada) harus ditempatkan. Proses ini menghasilkan hasil yang diurutkan dengan satu melewati data,

seperti yang diilustrasikan di sebelah kiri. Catatan: Dalam salah satu aplikasi kami, fakta bahwa

implementasi ini stabil sangatlah penting: item dengan kunci yang sama disatukan tetapi disimpan

dalam urutan yang relatif sama.


KEY-INDEXED COUNTING adalah metode penyortiran yang sangat efektif dan sering diabaikan
untuk aplikasi di mana kunci adalah bilangan bulat kecil. Memahami cara kerjanya adalah langkah
pertama untuk memahami pengurutan string. PROPOSISI A menyiratkan bahwa penghitungan yang
diindeks dengan kunci menembus batas bawah N log N yang kami buktikan untuk penyortiran.
Bagaimana cara melakukannya? PROPOSISI I di BAGIAN 2.2 adalah batas bawah jumlah perbandingan
yang diperlukan (ketika data diakses hanya melalui compareTo())-penghitungan indeks-kunci tidak
melakukan perbandingan (ia mengakses data hanya melalui kunci()). Ketika R berada dalam faktor
konstan N, kami memiliki pengurutan waktu linier.

Pengurutan string LSD Metode pengurutan string pertama yang kami pertimbangkan dikenal
sebagai pengurutan string yang paling tidak signifikan digit pertama (LSD). Pertimbangkan aplikasi
motivasi berikut: Misalkan seorang insinyur jalan raya membuat perangkat yang mencatat nomor plat
semua kendaraan yang menggunakan jalan raya yang sibuk untuk jangka waktu tertentu dan ingin
mengetahui jumlah kendaraan berbeda yang menggunakan jalan raya tersebut. Pelat nomor adalah
campuran antara angka dan huruf, jadi wajar untuk merepresentasikannya sebagai string. Dalam situasi
paling sederhana (seperti contoh plat nomor California di sebelah kanan) semua string memiliki jumlah
karakter yang sama. Situasi ini sering ditemukan dalam pengurutan aplikasi--misalnya, nomor telepon,
nomor rekening bank, dan alamat IP biasanya berupa string dengan panjang tetap.
Jika string masing masing memiliki panjang W, kami mengurutkan string W kali dengan
penghitungan indeks kunci, menggunakan masing-masing posisi sebagai kunci, mulai dari kanan ke kiri.
fakta ini dan lihat contoh saat mempelajari bukti kebenaran ini:

Konvensi akhir string.

Kita perlu memberikan perhatian khusus untuk menjangkau ujung string dalam pengurutan

string MSD. Untuk penyortiran yang tepat, kita memerlukan sub larik untuk string yang semua

karakternya telah diperiksa muncul sebagai subarray pertama, dan kita tidak ingin secara rekursif

mengurutkan subarray ini. Untuk memfasilitasi dua bagian perhitungan ini kami menggunakan metode

dua argumen toChar () pribadi untuk mengkonversi dari string yang diindeks karakter ke indeks array

yang mengembalikan -1 jika ditentukan posisi karakter melewati akhir string. Lalu, kita baru saja

tambahkan 1 ke setiap nilai yang dikembalikan, ke mendapatkan int nonnegatif bahwa kita dapat

digunakan untuk menghitung jumlah indeks[]. Ini konvensi berarti bahwa kita memiliki R+1

kemungkinan nilai karakter yang berbeda pada setiap posisi string: 0 untuk menandakan akhir string, 1

untuk karakter abjad pertama, 2 untuk abjad kedua

karakter, dan lain sebagainya.

Alfabet yang ditentukan biaya pengurutan string MSD sangat bergantung pada jumlah
kemungkinan karakter dalam alfabet. Sangat mudah untuk memodifikasi metode pengurutan kami untuk
mengambil Alphabet sebagai argumen, untuk memungkinkan peningkatan efisiensi pada klien yang
melibatkan string yang diambil dari huruf yang relatif kecil. Perubahan berikut akan melakukan pekerjaan:

 Simpan alfabet dalam variabel alfa instan di konstruktor.

 Atur R ke alfa. R() di konstruktor.

 Menggantikan.charAt (d) dengan alpha.toIndex(s.charAt(d)) di charAt().


2. TRIES
struktur data yang dibangun dari karakter kunci string yang memungkinkan kami menggunakan
karakter kunci pencarian untuk memandu pencarian. Nama "trie" adalah sedikit permainan kata yang
diperkenalkan oleh E. Fredkin pada tahun 1960 karena struktur data digunakan untuk pengambilan, tetapi
kami melafalkannya "mencoba" untuk menghindari kebingungan dengan "pohon". Kita mulai dengan
deskripsi tingkat tinggi tentang properti dasar percobaan, termasuk algoritma pencarian dan penyisipan,
lalu dilanjutkan ke detail representasi dan implementasi Java. Setiap node ditunjuk oleh hanya satu node
lain, yang disebut induknya (kecuali untuk satu node, root, yang tidak memiliki node yang menunjuk ke
sana), dan setiap node memiliki link R, di mana R adalah ukuran alfabet. Seringkali, percobaan memiliki
sejumlah besar tautan nol, jadi ketika kami menggambar sebuah trie, kami biasanya menghilangkan tautan
nol. Meskipun link menunjuk ke node, kita dapat melihat setiap link sebagai titik. ing ke trie, trie yang
akarnya adalah simpul yang direferensikan.
Setiap node ditunjuk oleh hanya satu node lain, yang disebut induknya (kecuali untuk satu node,
root, yang tidak memiliki node yang menunjuk ke sana), dan setiap node memiliki link R, di mana R
adalah ukuran alfabet. Seringkali, percobaan memiliki sejumlah besar tautan nol, jadi ketika kami
menggambar sebuah trie, kami biasanya menghilangkan tautan nol. Meskipun link menunjuk ke node, kita
dapat melihat setiap link sebagai titik. ing ke trie, trie yang akarnya adalah simpul yang direferensikan.
Setiap simpul di trie memiliki tautan yang sesuai dengan setiap karakter string yang mungkin.
Kami mulai dari root, lalu ikuti tautan yang terkait dengan karakter pertama di kunci; dari simpul itu kami
mengikuti tautan yang terkait dengan karakter kedua di kunci; dari simpul itu kami mengikuti tautan yang
terkait dengan karakter ketiga di kunci dan Pada titik ini, salah satu dari tiga kondisi berikut berlaku (lihat
gambar di atas untuk contoh):

 Nilai pada node yang sesuai dengan karakter terakhir pada kunci tidak null (seperti dalam
pencarian shell dan dia digambarkan di sebelah kiri atas). Hasil ini adalah hit pencarian - nilai
yang terkait dengan kunci adalah nilai dalam node yang sesuai dengan karakter terakhirnya.

 Nilai di simpul yang sesuai dengan karakter terakhir di kunci adalah nol (seperti dalam pencarian
shell yang digambarkan di kanan atas di atas). Hasil ini adalah pencarian yang hilang: kuncinya
tidak ada di tabel.

Pencarian diakhiri dengan tautan nol (seperti dalam pencarian pantai yang digambarkan di kanan
bawah di atas). Hasil ini juga merupakan miss pencarian. Dalam semua kasus, pencarian dilakukan hanya
dengan memeriksa node di sepanjang jalur dari root ke node lain dalam tri.
Percobaan pencarian ternary (TSTS) Untuk membantu kami menghindari biaya ruang yang berlebihan
terkait dengan trik R-way, kami sekarang mempertimbangkan representasi alternatif: trie pencarian
ternary (TST). Dalam TST, setiap node memiliki karakter, tiga link, dan nilai. Tiga tautan sesuai dengan
kunci yang karakter saat ini kurang dari, sama dengan, atau lebih besar dari karakter node. Dalam
percobaan R-way dari ALGORITMA 5.4, node tric diwakili oleh link R, dengan karakter yang sesuai
dengan setiap link non-null secara implisit diwakili oleh indeksnya. Dalam TST yang sesuai, karakter
muncul secara eksplisit di node--kami menemukan karakter yang sesuai dengan kunci hanya saat kami
melewati tautan tengah.
Properti TSTS TST adalah representasi ringkas dari trie R-way, tetapi dua struktur data memiliki sifat
yang sangat berbeda. Mungkin perbedaan yang paling penting adalah bahwa PROPERTI A tidak berlaku
untuk TSTS: representasi BST dari setiap simpul trie bergantung pada urutan penyisipan kunci, seperti
halnya BST lainnya.

Ruang angkasa. Properti paling penting dari TSTS adalah mereka hanya memiliki tiga tautan di setiap
node, sehingga TST membutuhkan ruang yang jauh lebih sedikit daripada trie yang sesuai.

 Proposisi J. Jumlah tautan dalam TST yang dibuat dari kunci string N dengan panjang rata-rata w
adalah antara 3N dan 3Nw

 Proposisi K. Sebuah pencarian miss di TST dibangun dari N kunci string acak membutuhkan -
Dalam karakter N membandingkan, rata-rata. Hasil pencarian atau penyisipan dalam TST
menggunakan perbandingan karakter untuk setiap karakter dalam kunci pencarian.

3. PENCARIAN SUBSTRING
OPERASI DASAR pada string adalah pencarian substring yang diberikan string teks dengan
panjang N dan string pola dengan panjang M, menemukan kemunculan pola di dalam teks. Sebagian
besar algoritma untuk masalah ini dapat dengan mudah diperluas untuk menemukan semua
kemunculan pola dalam teks, menghitung jumlah kemunculan pola dalam teks, atau menyediakan
konteks (substring teks yang mengelilingi setiap kemunculan pola).
Saat Anda mencari kata saat menggunakan editor teks atau browser web, Anda melakukan
pencarian substring. Memang, motivasi asli untuk masalah ini adalah untuk mendukung pencarian
tersebut. Aplikasi klasik lainnya adalah mencari beberapa pola penting dalam komunikasi yang
disadap. Seorang pemimpin militer mungkin tertarik untuk menemukan pola SERANGAN PADA
DAWN di suatu tempat dalam pesan teks yang dicegat; seorang hacker mungkin tertarik untuk
menemukan pola Password: di suatu tempat di memori komputer Anda. Di dunia sekarang ini, kita
sering menelusuri informasi dalam jumlah besar yang tersedia di web.

Untuk lebih menghargai algoritme, anggap polanya relatif pendek (dengan M sama dengan,
katakanlah, 100 atau 1.000) dan teksnya relatif panjang (dengan N sama dengan, katakanlah, 1 juta
atau 1 miliar). Dalam pencarian substring, kami biasanya melakukan praproses pola agar dapat
mendukung pencarian cepat untuk pola itu dalam teks Pencarian substring adalah masalah yang
menarik dan klasik: beberapa algoritma yang sangat berbeda (dan mengejutkan) telah ditemukan
yang tidak hanya menyediakan spektrum metode praktis yang berguna tetapi juga mengilustrasikan
spektrum teknik desain algoritma fundamental.

Pencarian substring Knuth-Morris-Pratt Ide dasar di balik algoritma ditemukan oleh Knuth,
Morris, dan Pratt adalah ini: setiap kali kita mendeteksi ketidakcocokan, kita sudah mengetahui
beberapa karakter dalam teks (karena mereka cocok dengan karakter pola sebelum ketidakcocokan).
Kita dapat memanfaatkan informasi ini untuk menghindari pencadangan penunjuk teks di atas
semua karakter yang diketahui. Sebagai contoh spesifik, misalkan kita memiliki alfabet dua karakter
dan sedang mencari pola BA A A A A A A A A. Sekarang, anggaplah kita mencocokkan lima
karakter dalam pola, dengan ketidakcocokan pada keenam. Ketika ketidakcocokan terdeteksi.
Seperti yang sering terjadi ketika kita memiliki beberapa algoritma untuk tugas yang sama,
masing-masing memiliki fitur yang menarik. Pencarian brute-force mudah diimplementasikan dan
bekerja dengan baik dalam kasus-kasus tipikal (metode indexOf() Java dalam String menggunakan
pencarian brute-force); Knuth-Morris-Pratt dijamin waktu linier tanpa cadangan input; Boyer-Moore
adalah sublinier (dengan faktor M) dalam situasi tipikal; dan Rabin-Karp adalah linier. Masing-
masing juga memiliki kelemahan: brute-force mungkin membutuhkan waktu yang sebanding
dengan MN; Knuth-Morris-Pratt dan Boyer-Moore menggunakan ruang ekstra; dan Rabin-Karp
memiliki loop dalam yang relatif panjang (beberapa operasi aritmatika, berbeda dengan
perbandingan karakter dalam metode lain). Karakteristik ini dirangkum dalam tabel di bawah ini.
4. EKSPRESI REGULER
R. A. Karp adalah pendekatan yang sama sekali berbeda untuk pencarian substring yang
didasarkan pada hashing. Kami menghitung fungsi hash untuk pola dan kemudian mencari kecocokan
dengan menggunakan fungsi hash yang sama untuk setiap kemungkinan substring karakter-M dari
teks. Jika kita menemukan substring teks dengan nilai hash yang sama dengan polanya, kita dapat
memeriksa kecocokannya. Proses ini sama dengan menyimpan pola dalam tabel hash, kemudian
melakukan pencarian untuk setiap substring teks, tetapi kita tidak perlu mencadangkan memori untuk
tabel hash karena hanya memiliki satu entri. Implementasi langsung berdasarkan deskripsi ini akan
jauh lebih lambat daripada pencarian brute-force (karena menghitung fungsi hash yang melibatkan
setiap karakter kemungkinan akan jauh lebih mahal daripada hanya membandingkan karakter), tetapi
Rabin dan Karp menunjukkan bahwa itu adalah mudah untuk menghitung fungsi hash untuk substring
karakter-M dalam waktu yang konstan (setelah beberapa pra-pemrosesan), yang mengarah ke
pencarian substring waktu-linear dalam situasi praktis.

Beberapa karakter, seperti \, 1., (, dan ), adalah karakter meta yang kita gunakan untuk membentuk
ekspresi reguler. Kami menggunakan urutan pelarian yang dimulai dengan karakter garis miring
terbalik \ memisahkan karakter meta dari karakter dalam alfabet. Urutan melarikan diri mungkin \
diikuti oleh satu karakter meta (yang mewakili karakter itu). Misalnya, \\ mewakili\. Urutan melarikan
diri lainnya mewakili karakter khusus dan spasi. Misalnya, \t mewakili karakter tab, \n mewakili baris
baru, dan \s mewakili karakter spasi apa pun.
5. KOMPRESI DATA
Semua jenis data yang kami proses dengan sistem komputer modern memiliki kesamaan:
mereka akhirnya direpresentasikan dalam biner. Kita dapat menganggap masing-masing hanya
sebagai urutan bit (atau byte). Untuk singkatnya, kami menggunakan istilah bitstream di bagian ini
untuk merujuk ke urutan bit dan bytestream ketika kami mengacu pada bit yang dianggap sebagai
urutan byte ukuran tetap. Bitstream atau bytestream mungkin disimpan sebagai file di komputer
Anda, atau mungkin berupa pesan yang dikirimkan di internet model dasar. Dengan demikian, model
dasar kami untuk kompresi data cukup sederhana, dua komponen utama, masing-masing kotak hitam
yang membaca dan menulis bitstream: Sebuah kotak kompres yang mengubah bitstream B menjadi
versi terkompresi C(B) Kotak luaskan yang mengubah C(B) kembali menjadi B.

Menggunakan notasi | BI untuk menunjukkan jumlah bit dalam aliran bit, kami tertarik untuk
meminimalkan kuantitas [C(B)]/[B], yang dikenal sebagai rasio kompresi.

Sebagian besar sistem saat ini, termasuk lava, mendasarkan I/O mereka pada aliran byte 8-bit, jadi
kita mungkin memutuskan untuk membaca dan menulis aliran byte untuk mencocokkan format I/O
dengan representasi internal dari tipe primitif, mengkodekan karakter 8-bit dengan I byte, short 16-bit
dengan 2 byte, int 32-bit dengan 4 byte, dan seterusnya. Karena aliran bit adalah abstraksi utama untuk
kompresi data, kami melangkah lebih jauh untuk memungkinkan klien membaca dan menulis bit
individual, bercampur dengan data bertipe primitif. Tujuannya adalah untuk meminimalkan kebutuhan
konversi tipe dalam program klien dan juga untuk menjaga konvensi sistem operasi untuk
merepresentasikan data. Kami menggunakan API berikut untuk membaca bitstream dari input standar.
Menggunakan pengkodean ASCII standar (1 byte, atau 8 bit per karakter), string ini adalah aliran
bit dengan panjang 8x35= 280. String semacam ini sangat penting dalam biologi modern, karena ahli
biologi menggunakan huruf A, C, T. dan C untuk mewakili empat nukleotida dalam DNA organisme
hidup. Genom adalah urutan nukleotida. Para ilmuwan tahu bahwa memahami sifat-sifat genom adalah
kunci untuk memahami proses yang memanifestasikan dirinya dalam organisme hidup, termasuk
kehidupan, kematian, dan penyakit.

Anda mungkin juga menyukai