Barisan Umum Terpanjang: Python, C++ Example

⚡ Ringkasan Cerdas

Longest Common Subsequence (LCS) mengidentifikasi pola elemen berurutan terpanjang yang dimiliki bersama oleh dua string tanpa memerlukan karakter yang berdekatan. Metode pemrograman dinamis klasik ini mendasari utilitas perbandingan (diff), penyelarasan DNA, dan kontrol versi dengan membandingkan sekuens secara efisien dalam waktu polinomial.

  • 📘 Konsep Inti: Longest Common Subsequence (LCS) mengembalikan himpunan karakter berurutan terpanjang yang muncul di kedua string input sambil mempertahankan urutan relatif aslinya.
  • 🐢 Pendekatan Naif: Metode brute force menghitung setiap suburutan dari string pertama dan membandingkannya dengan string kedua, berjalan dalam waktu eksponensial O(n·2^m).
  • 🔁 Metode Rekursif: Aturan rekursif mencocokkan karakter terakhir atau melakukan rekursi pada substring yang lebih kecil, tetapi menghitung ulang tumpang tindih.ping submasalah berulang kali.
  • 🧮 Pemrograman Dinamis: Tabel dp dua dimensi menyimpan hasil subproblem, menghasilkan solusi O(m·n) yang bersih dengan ruang tambahan O(m·n).
  • 🐍 Cakupan Bahasa: Menyelesaikan Python ke C++ Implementasi-implementasi tersebut mendemonstrasikan baik model dasar rekursif maupun tabel dp yang di-memoize untuk penggunaan praktis.
  • 🌐 Aplikasi Nyata: Longest Common Subsequence (LGBT) mendukung alat perbandingan (diff), pemeriksa plagiarisme, korektor ejaan, dan penyelarasan sekuens bioinformatika di seluruh DNA dan protein.

Urutan Umum Terpanjang

Apa Barisan Umum Terpanjang?

Longest Common Subsequence (LCS) berarti Anda akan diberikan dua string, pola, atau urutan objek. Di antara kedua urutan atau string ini, Anda perlu menemukan suburutan terpanjang dari elemen-elemen yang berada dalam urutan yang sama yang terdapat di kedua string atau pola tersebut.

Example

Sebagai contoh, terdapat dua string yang diberikan. Mari kita asumsikan bahwa:

Pola_1 = “RGBGARGA”
Pola_2 = “BGRARG”

  • Dari pattern_1, dapat dihasilkan urutan seperti “RGB”, “RGGA”, “RGAR”. Untuk membuat urutan, Anda perlu mempertahankan posisi relatif setiap karakter dalam string.
  • Dari pola_2, kita dapat menghasilkan urutan seperti “BGR”, “BRAG”, “RARG”. Urutan dapat dihasilkan selama mempertahankan posisi relatif string aslinya.

Istilah posisi relatif berarti keteraturan.

Sebagai contoh, “BRG” adalah urutan yang valid karena “B” muncul pertama, kemudian “R,” dan kemudian “G” dalam string asli pattern_2. Namun, jika urutannya adalah “RBRG”, maka itu tidak valid, karena dalam string asli (pattern_2), “B” muncul pertama.

Contoh string Longest Common Subsequence (LCS)

Kita mempunyai dua pilihan untuk mencari Barisan Persekutuan Terpanjang dari dua barisan atau larik yang diberikan.

  • Metode naif
  • Solusi Pemrograman Dinamis: Urutan Umum Terpanjang juga dikenal sebagai LCS.

Solusi sederhana memiliki kompleksitas waktu yang lebih besar dan bukan solusi optimal. Dengan menggunakan Solusi Pemrograman Dinamis (DP), kita dapat mengatasi masalah kompleksitas tersebut.

Metode Naif

Metode Naif adalah pendekatan sederhana untuk masalah ini, terlepas dari kompleksitas waktu dan faktor optimasi lainnya. Metode ini terdiri dari "brute force", banyak perulangan, dan panggilan rekursif dalam sebagian besar kasus. Istilah brute force berarti menelusuri semua pola yang mungkin untuk masalah tertentu.

Example

Dari contoh pattern1 dan pattern2 di atas, kita asumsikan pattern1 panjangnya m dan pattern2 panjangnya n. Untuk memeriksa setiap kemungkinan kasus, kita perlu mengevaluasi setiap kemungkinan rangkaian pola1 dengan pola2.

Berikut adalah contoh string sederhana 4 huruf “ABCD”. Misalnya, kita perlu membuat urutan dari “ABCD”. Kita bisa mengambil karakter atau tidak. Itu berarti, untuk setiap karakter, kita memiliki dua pilihan:

  • Karakter tersebut akan ditambahkan ke selanjutnya.
  • Karakter tidak akan ditambahkan ke selanjutnya.

Di sini, gambar menunjukkan semua barisan yang dapat kita buat dari string “ABCD”.

Urutan Metode Naif ABCD

Urutan dengan 1 karakter:

Metode Naif urutan karakter tunggal

Urutan dengan 2 karakter:

Metode Naif dua urutan karakter

Urutan dengan 3 karakter:

Metode Naif tiga urutan karakter

Dari diagram di atas, terdapat 14 urutan. Jika kita tidak mengambil huruf apa pun, pada dasarnya string kosong, maka total urutannya adalah 15. Selain itu, string “ABCD” itu sendiri merupakan sebuah urutan. Jadi, total urutannya adalah 16.

Jadi, dimungkinkan untuk menghasilkan 2^4 atau 16 suburutan dari “ABCD”. Kemudian, sebuah string dengan panjang m akan memiliki suburutan total sebanyak 2^m.

Untuk setiap suburutan, kita perlu memeriksanya untuk seluruh pola2. Ini akan membutuhkan waktu O(n). O(n) berarti fungsi kompleksitas yang menghitung waktu yang dibutuhkan untuk eksekusi.

Jadi, total kompleksitas waktu menjadi O(n*2^m). Untuk contoh yang telah kita lihat di atas, nilai m=8 dan n=5.

Berikut langkah-langkah Metode Naif:

Langkah 1) Ambil urutan dari pola 1.
Langkah 2) Cocokkan urutan dari langkah 1 dengan pola 2.
Langkah 3) Jika cocok, simpan selanjutnya.
Langkah 4) Jika masih ada urutan yang tersisa di pola1, maka ulangi langkah 1.
Langkah 5) Cetak urutan terpanjang.

Substruktur Optimal

Istilah substruktur optimal berarti solusi optimal dapat ditemukan dengan menyelesaikan submasalah. Misalnya, pada contoh di atas, kita memiliki pola1 dan pola2.

Langkah 1) Ambil dua karakter pertama dari setiap pola.

Langkah 2) Ambil karakter ketiga hingga kelima dari setiap pola.

Langkah 3) Lanjutkan hal yang sama dengan karakter yang tersisa.

Struktur Rekursif dari masalah LCS

Struktur Rekursif dari masalah LCS

Kita mencari LCS (Least Common Substance) pada substring (string yang dihasilkan dari string asli). Kemudian kita menyimpan catatan panjang LCS dari substring tersebut.

Sekarang, inilah properti menarik lainnya tumpang tindihping sub-masalahSuatu masalah dikatakan memiliki tumpang tindih.ping sub-masalah jika pernyataan masalah dapat dipecah menjadi sub-masalah kecil dan digunakan beberapa kali dalam program.

Diagram di bawah menunjukkan bahwa algoritma rekursif memanggil fungsi dengan parameter yang sama beberapa kali.

Tumpang tindih substruktur optimalping submasalah

Sebagai contoh, perhatikan pohon rekursi. Di dalam kotak berwarna gelap, Anda dapat melihat adanya tumpang tindih.ping sub-masalah. (“RG”, “RA”), (“RG”, “R”), dan lainnya dipanggil beberapa kali.

Untuk mengoptimalkan hal ini, kami memiliki pendekatan sebagai berikut: Pemrograman Dinamis (DP).

Metode Rekursif Urutan Bagian Terpanjang yang Sama

Grafik yang ditunjukkan di atas adalah metode rekursif. Setiap fungsi rekursif memiliki kasus dasar untuk menghentikan rekursi atau mulai mengembalikan nilai dari tumpukannya.

Untuk implementasi ini, kita akan menggunakan kasus dasar. Jadi, algoritma seperti berikut ini:

  • Jika semua elemen sebelum elemen terakhir memiliki kecocokan, maka tambahkan satu panjang dan kembali.
  • Berikan dua pola ke fungsi tersebut, dan ambil nilai maksimum dari nilai yang dikembalikan.
  • Jika suatu pola mempunyai panjang nol, maka kita tidak mempunyai urutan berikutnya untuk dibandingkan. Kembalikan 0 dalam kasus ini. Ini adalah kasus dasar dari rekursi.

Pseudo Code:

def lcs:
    input: pattern_1, pattern_2, len_1, len_2
    if len_1 or len_2 is zero:
        return 0
    if pattern_1[len_1 - 1] equals pattern_2[len_2 - 1]:
        return 1 + lcs(pattern_1, pattern_2, len_1 - 1, len_2 - 1)
    else:
        return max(lcs(pattern_1, pattern_2, len_1 - 1, len_2),
                   lcs(pattern_1, pattern_2, len_1, len_2 - 1))

Implementasi di C++

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int lcs(string pattern_1, string pattern_2, int len_1, int len_2) {
  if (len_1 == 0 || len_2 == 0)
    return 0;
  if (pattern_1[len_1 - 1] == pattern_2[len_2 - 1]) {
    return 1 + lcs(pattern_1, pattern_2, len_1 - 1, len_2 - 1);
  } else {
    return max(lcs(pattern_1, pattern_2, len_1 - 1, len_2), lcs(pattern_1, pattern_2, len_1, len_2 - 1));
  }
}
int main() {
  string pattern_1, pattern_2;
  pattern_1 = "RGBGARGA";
  pattern_2 = "BGRARG";
  cout<<"Length of LCS is: "<<lcs(pattern_1, pattern_2, pattern_1.size(), pattern_2.size())<<endl;
}

Keluaran:

Length of LCS is: 5

Implementasi di Python

def lcs(pattern_1, pattern_2, len_1, len_2):
    if len_1 == 0 or len_2 == 0:
        return 0
    if pattern_1[len_1 - 1] == pattern_2[len_2 - 1]:
        return 1 + lcs(pattern_1, pattern_2, len_1 - 1, len_2 - 1)
    else:
        return max(lcs(pattern_1, pattern_2, len_1 - 1, len_2),
                   lcs(pattern_1, pattern_2, len_1, len_2 - 1))

pattern_1 = "RGBGARGA"
pattern_2 = "BGRARG"
print("Length of LCS is: ", lcs(pattern_1, pattern_2, len(pattern_1), len(pattern_2)))

Keluaran:

Length of LCS is:  5

Metode Pemrograman Dinamis Urutan Bagian Umum Terpanjang (LCS)

Pemrograman dinamis berarti mengoptimalkan metode rekursif biasa. Misalnya, jika kita melihat grafik pendekatan rekursif atau naif, kita dapat melihat bahwa ada beberapa panggilan fungsi yang identik. Metode Pemrograman Dinamis mencatat semua perhitungan dalam sebuah array dan menggunakannya kembali saat dibutuhkan.

Kita akan menggunakan array 2D dengan dimensi mxn, di mana m dan n adalah panjang pola1 dan pola2. Untuk sebuah Array 2D, kita dapat menggunakan struktur data List di Python atau struktur data vektor/array di C++.

Pseudo Code untuk LCS menggunakan DP:

LCS(pattern_1, pattern_2):
    m = length of pattern_1 + 1
    n = length of pattern_2 + 1
    dp[n][m]
    for i in range 0 to n + 1:
        for j in range 0 to m + 1:
            if i or j equals to 0:
                dp[i][j] = 0
            else if pattern_1[i] == pattern_2[j]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    return dp[n][m]

Berikut adalah tabel LCS yang digunakan sebagai struktur data array 2D untuk pendekatan pemrograman dinamis.

Metode Pemrograman Dinamis untuk Tabel LCS 2D

Mari kita bahas logika yang kita gunakan di sini. Langkah-langkahnya adalah:

Langkah 1) Jika i atau j adalah nol, kita mengambil string kosong dari dua string yang diberikan dan mencoba menemukan suburutan yang sama. Namun, karena substring yang kita ambil kosong, panjang suburutannya adalah 0.

Langkah 2) Jika dua karakter cocok, kita akan menetapkan nilai ke indeks (i,j) dengan menaikkan LCS yang telah dihitung sebelumnya, yang ada di indeks (i-1,j-1) (dari baris sebelumnya).

Langkah 3) Jika tidak cocok, maka kita akan mengambil LCS maksimum dari dua indeks yang berdekatan. Dengan cara ini, kita perlu mengisi semua nilai dalam array 2D.

Langkah 4) Terakhir, kami akan mengembalikan nilai sel terakhir dari array 2D.

Pada dasarnya, semua nilai dalam larik 2D berisi panjang suburutan umum. Di antara nilai-nilai tersebut, sel terakhir berisi panjang suburutan umum terpanjang.

Implementasi di C++

#include<iostream>
using namespace std;
int lcs(string pattern_1, string pattern_2) {
  int m = pattern_1.size();
  int n = pattern_2.size();
  // dp will store solutions as the iteration goes on
  int dp[n + 1][m + 1];
  for (int i = 0; i < n + 1; i++) {
    for (int j = 0; j < m + 1; j++) {
      if (i == 0 || j == 0) {
        dp[i][j] = 0;
      } else if (pattern_2[i - 1] == pattern_1[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      } else {
        dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
      }
    }
  }
  return dp[n][m];
}
int main() {
  string pattern_1 = "RGBGARGA";
  string pattern_2 = "BGRARG";
  cout<<"Length of LCS: "<<lcs(pattern_1, pattern_2)<<endl;
}

Keluaran:

Length of LCS: 5

Implementasi di Python

def lcs(pattern_1, pattern_2):
    m = len(pattern_1)
    n = len(pattern_2)
    # dp will store solutions as the iteration goes on
    dp = [[None] * (n + 1) for item in range(m + 1)]
    for i in range(m + 1):
        for j in range(n + 1):
            if i == 0 or j == 0:
                dp[i][j] = 0
            elif pattern_1[i - 1] == pattern_2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    return dp[m][n]

pattern_1 = "RGBGARGA"
pattern_2 = "BGRARG"
print("Length of LCS: ", lcs(pattern_1, pattern_2))

Keluaran:

Length of LCS: 5

Jadi, kedua dawai tersebut mempunyai barisan persekutuan terpanjang dengan panjang 5.

Singkatnya, kita hanya menghitung setiap tugas sekali dalam metode DP. Dalam metode rekursif, mungkin ada tumpang tindih.ping submasalah.

Dalam Algoritma Pemrograman Dinamis ini, kami menggunakan matriks 2D. Akan ada dua string yang diberikan (anggap keduanya memiliki panjang n). Maka ruang yang dibutuhkan dalam array tersebut adalah nx n. Jika stringnya cukup besar, kita memerlukan versi solusi DP yang memorinya dioptimalkan.

Logika sederhana yang diambil dalam kode adalah:

  • Deklarasikan DP Array 2D[m][n].
  • Isi baris pertama dan kolom pertama array DP dengan 0.
  • Ambil i dan j untuk iterasi.
  • Jika pattern1[i] sama dengan pattern2[j], maka perbarui DP[i][j] = DP[i-1][j-1] + 1.
  • Jika pattern1[i] tidak sama dengan pattern2[j], maka DP[i][j] akan menjadi nilai maksimum antara DP[i-1][j] dan DP[i][j-1].
  • Lanjutkan hingga i dan j mencapai m dan n.
  • Elemen terakhir, DP[m-1][n-1], akan berisi panjangnya.

Di sini, hal ini disebut sebagai DP[m-1][n-1] karena indeks array dimulai dari 0.

Pertanyaan Umum Demo Slot

Pipeline pembelajaran mesin menggunakan LCS sebagai fitur kesamaan dalam klasifikasi teks, evaluasi sekuens-ke-sekuens, dan pendeteksi plagiarisme kode. LCS juga mendasari metrik bergaya BLEU dan ROUGE yang memberi skor pada teks yang dihasilkan terhadap keluaran referensi.

Ya. Asisten pengkodean AI seperti GitHub Copilot dan GPT dapat menghasilkan versi pemrograman rekursif dan dinamis dari LCS. Python, C++, atau JavaMereka juga dapat menambahkan memoisasi, mencetak suburutan sebenarnya, atau mengkonversi kode ke bentuk iteratif sesuai permintaan.

Substring harus berurutan, sedangkan suburutan hanya perlu mempertahankan urutan. Untuk “ABCDE”, “ACD” adalah suburutan yang valid tetapi bukan substring, sedangkan “BCD” adalah substring dan suburutan sekaligus.

Versi pemrograman dinamis berjalan dalam waktu dan ruang O(m·n), di mana m dan n adalah panjang dari dua urutan input. Versi rekursif biasa berjalan dalam waktu eksponensial O(2^(m+n)) dalam kasus terburuk.

LCS mendukung utilitas perbedaan file, penggabungan Git, penyelarasan urutan DNA dan protein dalam bioinformatika, deteksi plagiarisme, pemeriksa ejaan, dan alat sinkronisasi data yang harus mempertahankan urutan catatan yang sama.

Tabel standar membutuhkan ruang O(m·n). Optimasi dua baris bergulir mengurangi ruang menjadi O(min(m, n)) ketika Anda hanya membutuhkan panjangnya, meskipun merekonstruksi suburutan sebenarnya masih membutuhkan tabel lengkap.

Ya, rekursi murni berfungsi untuk string pendek tetapi menghitung ulang submasalah yang sama berkali-kali dan menjadi tidak praktis setelah 20 hingga 25 karakter. Menambahkan memoisasi atau tabel DP mengembalikan fungsi tersebut. trackinerja tabel.

Ya. Ide DP diperluas ke k urutan menggunakan tabel k-dimensi dengan waktu dan ruang O(n^k). Varian ini muncul dalam alat diff multi-file dan penyelarasan urutan ganda dalam bioinformatika.

Ringkaslah postingan ini dengan: