# HG changeset patch # User Steve Losh # Date 1541641113 18000 # Node ID fc16ddecf261943b5a20642ee90458e369ada698 # Parent b8745a9fccd422270c07b9100f4fcd094590c695# Parent f65ed39c371d9381ccbac9f5e49bd783861e6e34 Merge. diff -r f65ed39c371d -r fc16ddecf261 rosalind.asd --- a/rosalind.asd Mon Nov 05 19:49:03 2018 -0500 +++ b/rosalind.asd Wed Nov 07 20:38:33 2018 -0500 @@ -43,5 +43,6 @@ (:file "grph") (:file "prtm") (:file "mrna") - (:file "splc"))))))) + (:file "splc") + (:file "lcsm"))))))) diff -r f65ed39c371d -r fc16ddecf261 src/problems/lcsm.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/problems/lcsm.lisp Wed Nov 07 20:38:33 2018 -0500 @@ -0,0 +1,79 @@ +(in-package :rosalind) + +;; This one sucked. We should use suffix trees some day but I just hacked this +;; together for now. + +(defparameter *input-lcsm* ">Rosalind_1 +GATTACA +>Rosalind_2 +TAGACCA +>Rosalind_3 +ATACA") + +(defun compute-substring-table% (string1 string2) + ;; Compute the table of substring lengths. + ;; + ;; a b c d e c + ;; x 0 0 0 0 0 0 + ;; a 1 0 0 0 0 0 + ;; b 0 2 0 0 0 0 + ;; c 0 0 3 0 0 1 + ;; e 0 0 0 0 0 1 + (check-type string1 string) + (check-type string2 string) + (let ((table (make-array (list (length string1) (length string2))))) + (dotimes (i1 (length string1)) + (dotimes (i2 (length string2)) + (setf (aref table i1 i2) + (if (char= (aref string1 i1) (aref string2 i2)) + (if (or (zerop i1) (zerop i2)) + 1 + (1+ (aref table (1- i1) (1- i2)))) + 0)))) + table)) + +(defun-inline substring-at% (string index length) + ;; Given a matching string/table-index and a length, return the substring. + ;; The table effectively stores (start, end] but subseq wants [start, end). + (let ((end (1+ index))) + (subseq string (- end length) end))) + +(defun-inline find-substrings-of-length% (table string1 length) + ;; Find all the substrings in the table with the given length. + (iterate (for (l i1 nil) :in-array table) + (when (= length l) + (collect (substring-at% string1 i1 length))))) + +(defun-inline find-maximum-length% (table) + ;; Find the highest length in the table. + (iterate (for l :across-flat-array table) + (maximizing l))) + +(defun longest-common-substrings (string1 string2) + "Return a list of the longest common substrings of `string1` and `string2`." + (let ((table (compute-substring-table% string1 string2))) + (find-substrings-of-length% table string1 (find-maximum-length% table)))) + +(defun longest (strings) + "Return a list of the longest strings in `strings`." + (remove-if-not + (curry #'= (alexandria:extremum (mapcar #'length strings) #'>)) + strings :key #'length)) + +(defun longest-common-substrings-of-any (substrings string) + "Return the longest common substrings of `string` and any one of `substrings`." + (-<> (iterate + (for substring :in substrings) + (appending (longest-common-substrings substring string))) + longest + (remove-duplicates <> :test #'string=))) + +(define-problem lcsm (data stream) + *input-lcsm* + "AC" + (let ((lines (iterate (for (nil line) :in-fasta data) + (collect line)))) + (-<> (reduce #'longest-common-substrings-of-any (rest lines) + :initial-value (list (first lines))) + (sort <> #'string<) ; tests + first))) diff -r f65ed39c371d -r fc16ddecf261 src/utils.lisp --- a/src/utils.lisp Mon Nov 05 19:49:03 2018 -0500 +++ b/src/utils.lisp Wed Nov 07 20:38:33 2018 -0500 @@ -228,7 +228,7 @@ ;;;; Testing ------------------------------------------------------------------ (defmacro define-test (problem input output &optional (test 'string=)) `(test ,(symb 'test- problem) - (is (,test ,output (,problem ,input))))) + (is (,test ,output (aesthetic-string (,problem ,input)))))) (defun run-tests () (1am:run)) @@ -246,7 +246,7 @@ (setf ,arg ,(ecase type (string `(ensure-string ,arg)) (stream `(ensure-stream ,arg)))) - (aesthetic-string (progn ,@body))) + (progn ,@body)) (setf (get ',symbol 'rosalind-name) ,(string-downcase name)) (define-test ,symbol ,sample-input ,sample-output) ',symbol)))) @@ -256,7 +256,7 @@ (defun solve% (problem) (with-open-file (input (problem-data-path problem)) - (pbcopy (funcall problem input)))) + (pbcopy (aesthetic-string (funcall problem input))))) (defmacro solve (name) `(solve% ',(symb 'problem- name)))