Menghitung substring umum terpanjang dari dua string menggunakan array suffix

15

Setelah saya belajar bagaimana membangun array sufiks dalam kompleksitas , saya tertarik untuk menemukan aplikasi array sufiks. Salah satunya adalah menemukan substring umum terpanjang antara dua string, dalam waktu . Saya menemukan di internet algoritma berikut:O(N)O(N)

  1. menggabungkan kedua string dan menjadi satu stringABAB
  2. hitung susunan sufiksAB
  3. hitung larik (awalan umum terpanjang)LCP
  4. jawabannya adalah nilai terbesarLCP[i]

Saya mencoba mengimplementasikannya, tetapi karena banyak detail implementasi tidak disebutkan (yaitu ketika menggabungkan string, haruskah saya menempatkan karakter khusus di antara mereka ( )?), Kode saya gagal pada banyak kasus uji. Bisakah seseorang menguraikan lebih lanjut tentang algoritma ini?AcB

Terima kasih sebelumnya.

Catatan: Saya tidak menjamin kebenaran dari algoritma ini; Saya menemukannya di blog, dan saya tidak yakin itu berfungsi. Jika menurut Anda itu salah, harap sarankan algoritma lain.

Rontogiannis Aristofanis
sumber
3
Sebelum menerapkan algoritme, cobalah untuk memahami mengapa itu bekerja. Itu bisa membantu menjawab pertanyaan seperti bagaimana menggabungkan dua string.
Yuval Filmus
3
Saya meragukan kebenaran dari algoritma ini. Ambil dan b c d , cara saya membacanya akan mengembalikan a b c d , yang salah. abcdabcdbcdabcd
Khaur

Jawaban:

20

Algoritme Anda salah . Saya berasumsi Anda tahu bagaimana menghitung array suffix dan array LCP dari sebuah string, yaitu implementasi yang efisien. Seperti yang telah ditunjukkan dalam komentar, Anda harus mencoba memahami apa yang masing-masing komponen, dan mengapa itu bekerja.

Pertama-tama, adalah array suffix ( ) dari sebuah string. Susunan sufiks pada dasarnya adalah semua sufiks dari string S yang disusun dalam urutan leksikografis naik. Lebih khusus, nilai S A [ i ] menunjukkan bahwa akhiran dari S mulai dari posisi S A [ i ] adalah peringkat saya dalam pemesanan leksikografis semua akhiran dari S .SASSA[i]SSA[i]iS

Berikutnya adalah array L C P [ i ] menunjukkan panjang awalan umum terpanjang antara sufiks mulai dari S A [ i - 1 ] dan S A [ i ] . Yaitu, ia melacak panjang awalan umum terpanjang di antara dua sufiks S berturut-turut ketika disusun dalam urutan leksikografis.LCPLCP[i]SA[i1]SA[i]S

Sebagai contoh, perhatikan string . Sufiks dalam susunan leksikografis adalah { a , a b b a b c a , a b c a , b a b c a , b b a b c a , b c a , c a } , jadi S A = [ 7 , 1S=abbabca{a,abbabca,abca,babca,bbabca,bca,ca} untuk array 1-diindeks. The L C P array yang akan L C P = [ - , 1 , 2 , 0 , 1 , 1 , 0 ] .SA=[7,1,4,3,2,5,6]LCPLCP=[,1,2,0,1,1,0]

Sekarang, diberikan dua string dan B , kita menggabungkan mereka sebagai S = A # B , di mana # adalah karakter tidak hadir di kedua A dan B . Alasan untuk memilih karakter seperti itu adalah bahwa ketika menghitung LCP dari dua sufiks, katakan a b # d a b d dan a b d , perbandingan akan terputus pada akhir string pertama (karena hanya terjadi sekali, dua sufiks yang berbeda tidak akan pernah berada di posisi yang sama), dan tidak akan "meluap" ke string lain.ABS=A#B#ABab#dabdabd

Sekarang, dapat dilihat bahwa Anda harus dapat melihat mengapa Anda hanya perlu melihat nilai-nilai berturut-turut dalam array (argumen didasarkan pada kontradiksi dan fakta bahwa sufiks dalam S A berada dalam urutan leksikografis). Terus periksa array L C P untuk nilai maksimum sehingga dua sufiks yang dibandingkan tidak menjadi milik string asli yang sama. Jika mereka tidak termasuk string asli yang sama (satu dimulai pada A dan yang lain dalam B ), maka nilai terbesar adalah panjang substring umum terbesar.LCPSALCPAB

Sebagai contoh, pertimbangkan dan B = b c . Kemudian, S = a b c a b c # b c . Sufiks yang diurutkan adalah { a b c # b c , a b c a b c # b c , b c , b c # b c , b c aA=abcabcB=bcS=abcabc#bc{abc#bc,abcabc#bc,bc,bc#bc,bcabc#bc,c,c#bc,cabc#bc}.
SA=[4,1,8,5,2,9,6,3,7]LCP=[,3,0,2,2,0,1,1,0]

Now, the greatest value is LCP[2]=3, but it is for SA[1] and SA[2], both of which start in the string A. So, we ignore that. On the other hand, LCP[4]=2 is for SA[3] (corresponds to the suffix bc of B) and SA[4] (corresponding to suffix bcabc#bc of A). So, this is the longest common substring between the two strings. For getting the actual substring, you take a length 2 (value of the greatest feasible LCP) substring starting from either SA[3] or SA[4], which is bc.

Paresh
sumber
1
Excellent explanation but I think that the example is a bit wrong, the sorted suffixes are : {#bc,abc#bc,abcabc#bc,bc,bc#bc,bcabc#bc,c,c#bc,cabc#bc}, SA=[7,4,1,8,5,2,9,6,3] and LCP=[−,0,3,0,2,2,0,1,1]
Saúl Martínez Vidals
1

The algorithm you found online is not entirely correct. As mentioned by Paresh, it will fail in the example given by him.

However, if you ensure that while checking the LCP, you only check the LCP of substrings of different strings. For example, if you are finding the LCS of strings A and B, then you need to ensure that the adjacent entries of the Suffix Array while checking for LCP are both not from the same string.

More details here.

rohitjv
sumber
1
When you say "This answer", do you mean your own answer or some other answer? Please only use the answer box to answer the question, not to comment on other answers. When you've picked up enough reputation, you'll be able to leave comments on other answers.
David Richerby
0

I think something like the algorithm you cite should indeed work if a character that is not part of the character set is used as a separator, and the suffix/prefix arrays are built to exclude all strings that contain the separator, probably the intention of the designer. this is basically equivalent to building suffix/prefix arrays for the two separate strings.

it would be helpful for future ref if you posted a link to the algorithm. note that wikipedia has the algorithm for this in pseudocode & many other algorithms. and there are implementations in most standard languages available online.

vzn
sumber