(* * * * * * * * * * *
* Resource Aware ML *
* * * * * * * * * * *
*
* File:
* longestCommonSubsequence.raml
*
* Author:
* Jan Hoffmann (2010)
*
* Resource Bound:
* O(n*m)
*
* Description:
* A standard example of dynamic programming that can be found in
* many textbooks (see e.g. Cormen/Leiserson/Rivest/Stein:
* Introduction to Algorithms) is the computation of the length of
* the longest common subsequence (LCS) of two given lists
* (sequences). Given two sequences a_1,...,a_n and b_1,...,b_m, one
* successively fills an nxm-matrix (here a list of lists) A such
* that A(i,j) contains the length of the LCS of a_1,...,a_i and
* b_1,...,b_j. It is the case that
*
* { 0 if i=0 or j=0
* A(i,j) = { A(i-1,j-1) + 1 if i,j > 0 and a_i = b_j
* { max(A(i,j-1),A(i-1,j)) if i,j > 0 and a_i \= b_j
*
* This algorithm can be analyzed in our system and is exemplary for
* similar algorithms that use dynamic programming.
*)
lcs : (L(int),L(int)) -> int
(* computes the length of the longest common subsequence *)
lcs(l1,l2) = let _=tick(1) in
let m = lcstable(l1,l2) in
match m with
| nil -> +0
| (l1::_) -> match l1 with
| nil -> +0
| (len::_) -> len;
(*
eq(lcs(A,B),1,[lcstable(A,B)],[A>=0,B>=0]).
*)
lcstable : (L(int),L(int)) -> L(L(int))
(* computes the table of lengths *)
lcstable (l1,l2) = let _=tick(1) in
match l1 with
| nil -> [firstline l2]
| (x::xs) -> let m = lcstable (xs,l2) in
match m with
| nil -> nil
| (l::ls) -> (newline (x,l,l2))::l::ls;
(*
eq(lcstable(A,B),1,[firstline(B)],[A=0]).
eq(lcstable(A,B),1,[newline(X,B,B),lcstable(Ap,B)],[A>=1,Ap=A-1]).
eq(out1_lcstable(A,B),1,[],[A=0]).
eq(out1_lcstable(A,B),1,[out1_lcstable(Ap,B)],[A>=1,Ap=A-1]).
eq(out2_lcstable(A,B),nat(B),[],[A=0]).
eq(out2_lcstable(A,B),0,[out2_lcstable(Ap,B)],[A>=1,Ap=A-1]).
*)
firstline : L(int) -> L(int)
(* Returns the first line of zeros *)
firstline(l) = let _=tick(1) in
match l with
| nil -> nil
| (x::xs) -> +0::firstline xs;
(*
eq(firstline(A),1,[],[A=0]).
eq(firstline(A),1,[firstline(Ap)],[A >=1,Ap=A-1]).
*)
newline : (int,L(int),L(int)) -> L(int)
(* computes a new line according to the recursive definition above
* y is the element of the second list
* lastline the the previously computed line in the matrix
* l contains elements of the first list *)
newline (y,lastline,l) = let _=tick(1) in
match l with
| nil -> nil
| (x::xs) -> match lastline with
| nil -> nil
| (belowVal::lastline') ->
let nl = newline(y,lastline',xs) in
let rightVal = right nl in
let diagVal = right lastline' in
let elem = if x == y then diagVal+1 else max(belowVal,rightVal)
in elem::nl;
(*
eq(newline(A,B,C),1,[],[C=0]).
eq(newline(A,B,C),1,[],[C>=1,B=0]).
eq(newline(A,B,C),1,[right(Cp),right(Bp),max(E,F),newline(A,Bp,Cp)],[C>=1,B>=1,Bp=B-1,Cp=C-1]).
eq(newline(A,B,C),1,[right(Cp),right(Bp),newline(A,Bp,Cp)],[C>=1,B>=1,Bp=B-1,Cp=C-1]).
eq(out_newline(A,B,C),0,[],[C=0]).
eq(out_newline(A,B,C),0,[],[C>=1,B=0]).
eq(out_newline(A,B,C),1,[out_newline(A,Bp,Cp)],[C>=1,B>=1,Bp=B-1,Cp=C-1]).
*)
right : L(int) -> int
right l = let _=tick(1) in
match l with
| nil -> +0
| (x::xs) -> x;
(*
eq(right(A),1,[],[]).
*)
max : (int,int) -> (int)
max (a,b) = let _=tick(1) in
if a>b then a else b;
(*
eq(max(A,B),1,[],[]).
*)