См. также |
‹ David Eppstein. Longest Common Subsequence (источник на английском языке) ‹ Общие подпоследовательности. Дистанция (в том числе перевод работы Дэвида Эпштайна, но с ошибками) |
Вариант 3-го практического задания для групп 110, 111 (2006)
Реализовать:
Для начала необходимо построить таблицу различий. Пример на псевдокоде:
{ A. B - входные последовательности m, n - длина A и B соответственно L - матрица размера m x n } выделить память для матрицы L; for i := m downto 0 do for j := n downto 0 do begin if ( A[i] == 0 ) or ( B[j] == 0 ) L[i,j] := 0; else if A[i+1] = B[j+1] L[i,j] := 1 + L[i+1, j+1]; else L[i,j] := max ( L[i+1, j], L[i, j+1] ); end; { в L[0,0] будет длина максимальной общей подпоследовательности }
После построения матрицы L для получения самой общей подпоследовательности достаточно выполнить следующий проход:
S := ''; i := 0; j := 0; while ( i < m ) and ( j < n ) do begin if A[i] = B[j] then begin S := S + A[i]; i := i + 1; j := j + 1; end else if L[i+1,j] >= L[i,j+1] i := i + 1; else j := j + 1; end
Для решения варианта б) предлагается разбить входной текста на строки, вычислить контрольную сумму (КС) для каждой строки, и проводить поиск самой длинной общей подпоследовательности уже в пространстве строк.
Чтобы КС с приемлемой вероятностью идентифицировала строки, необходимо подобрать подходящий алгоритм. Предлагается реализовать алгоритм вычисления циклического избыточного кода CRC-16 (Cyclic Redundancy Code).
Принцип вычисления CRC основан на выполнении деления на некоторый целый делитель длинного целого делимого, в качестве которого выступает исходная последовательность данных. При этом первый символ последовательности выступает в качестве старшего байта «большого целого» и т.д. все остальные байты. Тогда на примере CRC-4 вычисление циклического избыточного кода можно проиллюстрировать схемой «деления» в столбик.
A A+1 _ 1111 0000 | 10010 — делитель 1001 0 ________ _ 110 00 100 10 ________ _ 01 100 00 000 ________ _ 1 1000 1 0010 _______ 0110 — это и есть CRC-4
Надо заметить, что, во-первых, запоминать частное не нужно, важен только остаток. А во-вторых, количество бит делителя на единицу больше, чем разрядность самого циклического кода, и старший бит делителя всегда установлен. Это требование определяет разрядность вычисляемого избыточного кода, так как при его невыполнении получится меньший диапазон значений «остатков».
Выбор делителя — важная задача, подробно рассмотренная в [2]. Наиболее популярными являются значения:
При реализации варианта а) для отображения различий достаточно вывести две исходные строки выравненные по вхождениям общей подпоследовательности. Например, пусть введены две строки «x := tg(a);» и «y := arcsin(b);». Тогда вывод различий может выглядеть следующим образом:
x := tg....(a); |||| | || y := arcsin(b);
В варианте б) вывод различий двух текстов можно оформить горизонтальными разделителями. Например, пусть даны два текста:
i := 1; for j := 1 to 10 do i := i * j; writeln;
// i := 1; readln (i); for j := 1 to 10 do i := i * j; writeln (i);
Тогда вывод различий может выглядеть так:
——————————————————————-(1) 001 i := 1; ——————————————————————-(2) 001 // i := 1; 002 readln (i); ———————————————————————- ====================================================================== 002 003 for j := 1 to 10 do 003 004 i := i * j; ====================================================================== ——————————————————————-(1) 004 writeln; ——————————————————————-(2) 005 writeln (i); ———————————————————————-
При тестировании рекомендуется использовать перенаправление ввода-вывода операционной системы.