Пошук найдовшої спільної підпослідовності (англ. longest common subsequence, LCS) — це завдання пошуку послідовності, яка є підпослідовністю кількох послідовностей (зазвичай — двох). Часто завдання визначається як пошук всіх найбільших спільних підпослідовностей. Ця задача відрізняється від пошуку найдовшого спільного підрядка: на відміну від підрядків, підпослідовності не повинні займати суміжні позиції в оригінальних послідовностях. Це класична задача інформатики, яка має застосування, зокрема, в задачі порівняння текстових файлів (утиліта diff), а також у біоінформатиці.
Підпослідовність можна отримати з деякої послідовності, якщо видалити з неї деяку множину елементів (можливо, порожню). Наприклад, BCDB є підпослідовністю послідовності ABCDBAB. Також вона буде підпослідовністю послідовності XBXCDXBX. Послідовність Z є спільною підпослідовність послідовностей X і Y, якщо Z є підпослідовністю як X, так і Y. Потрібно для двох послідовностей X і Y знайти спільну підпослідовність найбільшої довжини. Зауважимо, що таких підпослідовностей може бути кілька.
Вирішення задачі
Порівняємо два методи рішення: повний перебір і динамічне програмування.
Повний перебір
Існують різні підходи при вирішенні даної задачі. Один з них — повний перебір. Ми порівнюємо кожен елемент рядка X з кожним елементом рядка Y, тобто — час роботи такого алгоритму.
Метод динамічного програмування
A | B | C | B | ||
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | |
D | 0 | ← 0 | ← 0 | ← 0 | ← 0 |
C | 0 | ← 0 | ← 0 | ↖ 1 | ← 1 |
B | 0 | ← 0 | ↖ 1 | ← 1 | ↖ 2 |
A | 0 | ↖ 1 | ← 1 | ← 1 | ↑ 2 |
Спочатку знайдемо довжину найбільшої підпослідовності. Припустимо, ми шукаємо рішення для випадку (n1, n2), де n1, n2 — довжина першого та другого рядків. Нехай вже існують рішення для всіх підзадач (m1, m2), менших заданої. Тоді задача (n1, n2) зводиться до підзадач наступним чином:
Тепер повернемося до задачі побудови підпослідовності. Для цього в наявний алгоритм для кожної задачі додають запам'ятовування тих підзадач, через які вона вирішується. Наступною дією, починаючи з останнього елемента, піднімаємося до початку за напрямками, заданим першим алгоритмом, і записуємо символи в кожній позиції. Це і буде відповіддю в цій задачі.
Очевидно, що час роботи алгоритму буде [].
Реалізація алгоритму
string getLongestCommonSubsequence(const string& a, const string& b) { vector<vector<int> > max_len; max_len.resize(a.size() + 1); for(int i = 0; i <= static_cast<int>(a.size()); i++) max_len[i].resize(b.size() + 1); for(int i = static_cast<int>(a.size()) - 1; i >= 0; i--) { for(int j = static_cast<int>(b.size()) - 1; j >= 0; j--) { if(a[i] == b[j]) { max_len[i][j] = 1 + max_len[i+1][j+1]; } else { max_len[i][j] = max(max_len[i+1][j], max_len[i][j+1]); } } } string res; for(int i = 0, j = 0; max_len[i][j] != 0 && i < static_cast<int>(a.size()) && j < static_cast<int>(b.size()); ) { if(a[i] == b[j]) { res.push_back(a[i]); i++; j++; } else { if(max_len[i][j] == max_len[i+1][j]) i++; else j++; } } return res; }
#>> a = "aaaaabbbb34354354345" #>> b = "abbb34aaabbbb" #>> longest_common_subsequence(a, b) #=> "aaaabbbb" def longest_common_subsequence(a, b) max_len = Array.new(a.size + 1, 0) max_len.map! {Array.new(b.size + 1, 0)} (a.size - 1).downto(0) do |i| (b.size - 1).downto(0) do |j| if a[i] == b[j] max_len[i][j] = 1 + max_len[i+1][j+1] else max_len[i][j] = [max_len[i+1][j], max_len[i][j+1]].max end end end res = "" i = 0 j = 0 while max_len[i][j] != 0 && i < a.size && j < b.size if a[i] == b[j] res << a[i] i += 1 j += 1 else if max_len[i][j] == max_len[i+1][j] i += 1 else j += 1 end end end res end
Джерела
- Томас Кормен; Чарльз Лейзерсон, Рональд Рівест, Кліфорд Стайн (2009) [1990]. 4.1 Задача пошуку найбільшого підмасиву. Вступ до алгоритмів (вид. 3rd). MIT Press і McGraw-Hill. ISBN .
Примітки
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Poshuk najdovshoyi spilnoyi pidposlidovnosti angl longest common subsequence LCS ce zavdannya poshuku poslidovnosti yaka ye pidposlidovnistyu kilkoh poslidovnostej zazvichaj dvoh Chasto zavdannya viznachayetsya yak poshuk vsih najbilshih spilnih pidposlidovnostej Cya zadacha vidriznyayetsya vid poshuku najdovshogo spilnogo pidryadka na vidminu vid pidryadkiv pidposlidovnosti ne povinni zajmati sumizhni poziciyi v originalnih poslidovnostyah Ce klasichna zadacha informatiki yaka maye zastosuvannya zokrema v zadachi porivnyannya tekstovih fajliv utilita diff a takozh u bioinformatici Priklad porivnyannya dvoh versij fajlu na osnovi yih najdovshoyi spilnoyi pidposlidovnosti chornij Pidposlidovnist mozhna otrimati z deyakoyi poslidovnosti yaksho vidaliti z neyi deyaku mnozhinu elementiv mozhlivo porozhnyu Napriklad BCDB ye pidposlidovnistyu poslidovnosti ABCDBAB Takozh vona bude pidposlidovnistyu poslidovnosti XBXCDXBX Poslidovnist Z ye spilnoyu pidposlidovnist poslidovnostej X i Y yaksho Z ye pidposlidovnistyu yak X tak i Y Potribno dlya dvoh poslidovnostej X i Y znajti spilnu pidposlidovnist najbilshoyi dovzhini Zauvazhimo sho takih pidposlidovnostej mozhe buti kilka Virishennya zadachiPorivnyayemo dva metodi rishennya povnij perebir i dinamichne programuvannya Povnij perebir Isnuyut rizni pidhodi pri virishenni danoyi zadachi Odin z nih povnij perebir Mi porivnyuyemo kozhen element ryadka X z kozhnim elementom ryadka Y tobto 2 n displaystyle 2 n chas roboti takogo algoritmu Metod dinamichnogo programuvannya A B C B 0 0 0 0 0 D 0 0 0 0 0 C 0 0 0 1 1 B 0 0 1 1 2 A 0 1 1 1 2 Spochatku znajdemo dovzhinu najbilshoyi pidposlidovnosti Pripustimo mi shukayemo rishennya dlya vipadku n1 n2 de n1 n2 dovzhina pershogo ta drugogo ryadkiv Nehaj vzhe isnuyut rishennya dlya vsih pidzadach m1 m2 menshih zadanoyi Todi zadacha n1 n2 zvoditsya do pidzadach nastupnim chinom f n 1 n 2 0 n 1 0 n 2 0 f n 1 1 n 2 1 1 s n 1 s n 2 m a x f n 1 1 n 2 f n 1 n 2 1 s n 1 s n 2 displaystyle f n 1 n 2 left begin array ll 0 amp n 1 0 lor n 2 0 f n 1 1 n 2 1 1 amp s n 1 s n 2 max f n 1 1 n 2 f n 1 n 2 1 amp s n 1 neq s n 2 end array right Teper povernemosya do zadachi pobudovi pidposlidovnosti Dlya cogo v nayavnij algoritm dlya kozhnoyi zadachi dodayut zapam yatovuvannya tih pidzadach cherez yaki vona virishuyetsya Nastupnoyu diyeyu pochinayuchi z ostannogo elementa pidnimayemosya do pochatku za napryamkami zadanim pershim algoritmom i zapisuyemo simvoli v kozhnij poziciyi Ce i bude vidpoviddyu v cij zadachi Ochevidno sho chas roboti algoritmu bude O n 1 n 2 displaystyle mathrm O n 1 cdot n 2 dzherelo Realizaciya algoritmuS string getLongestCommonSubsequence const string amp a const string amp b vector lt vector lt int gt gt max len max len resize a size 1 for int i 0 i lt static cast lt int gt a size i max len i resize b size 1 for int i static cast lt int gt a size 1 i gt 0 i for int j static cast lt int gt b size 1 j gt 0 j if a i b j max len i j 1 max len i 1 j 1 else max len i j max max len i 1 j max len i j 1 string res for int i 0 j 0 max len i j 0 amp amp i lt static cast lt int gt a size amp amp j lt static cast lt int gt b size if a i b j res push back a i i j else if max len i j max len i 1 j i else j return res Ruby gt gt a aaaaabbbb34354354345 gt gt b abbb34aaabbbb gt gt longest common subsequence a b gt aaaabbbb def longest common subsequence a b max len Array new a size 1 0 max len map Array new b size 1 0 a size 1 downto 0 do i b size 1 downto 0 do j if a i b j max len i j 1 max len i 1 j 1 else max len i j max len i 1 j max len i j 1 max end end end res i 0 j 0 while max len i j 0 amp amp i lt a size amp amp j lt b size if a i b j res lt lt a i i 1 j 1 else if max len i j max len i 1 j i 1 else j 1 end end end res endDzherelaTomas Kormen Charlz Lejzerson Ronald Rivest Kliford Stajn 2009 1990 4 1 Zadacha poshuku najbilshogo pidmasivu Vstup do algoritmiv vid 3rd MIT Press i McGraw Hill ISBN 0 262 03384 4 Primitki