Dynamic programming
3 problems from Rosalind — Bioinformatics Stronghold. Press Run on any block to execute it in your browser.
LGIS — Longest Increasing Subsequence
solvedbrowser + CLI Problem statement Open in the workbench Download .bl
Longest increasing subsequence, which in a genomic setting is how conserved order is recovered from a comparison of two genomes: positions that stay in ascending order form a synteny block, and the rest is rearrangement.
# Rosalind: LGIS — Longest Increasing Subsequence
# https://rosalind.info/problems/lgis/
#
# Given: A positive integer n and a permutation of length n.
# Return: A longest increasing subsequence, then a longest decreasing one.
let permutation = [5, 1, 4, 2, 3]
# O(n^2) dynamic programme: best[i] is the length of the longest run ending at
# i, and prev[i] remembers which index it came from so the run can be rebuilt.
fn longest_run(values, increasing) {
let n = len(values)
let best = range(0, n) |> map(|_| 1)
let prev = range(0, n) |> map(|_| -1)
let i = 1
while i < n {
let j = 0
while j < i {
let ordered = if increasing then values[j] < values[i] else values[j] > values[i]
if ordered and best[j] + 1 > best[i] {
best = set_at(best, i, best[j] + 1)
prev = set_at(prev, i, j)
}
j = j + 1
}
i = i + 1
}
let last = 0
let k = 1
while k < n {
if best[k] > best[last] then last = k
k = k + 1
}
let run = []
let at = last
while at >= 0 {
run = concat([values[at]], run)
at = prev[at]
}
run
}
fn set_at(list, index, value) {
range(0, len(list)) |> map(|i| if i == index then value else list[i])
}
let increasing = longest_run(permutation, true)
let decreasing = longest_run(permutation, false)
println("Increasing: " + (increasing |> map(|x| str(x)) |> join(" ")))
println("Decreasing: " + (decreasing |> map(|x| str(x)) |> join(" ")))
println("Expected: 1 2 3 / 5 4 2")
fn test_lgis_longest_runs() {
assert len(increasing) == 3, "LGIS: increasing length " + str(len(increasing))
assert len(decreasing) == 3, "LGIS: decreasing length " + str(len(decreasing))
}
LCSQ — Finding a Shared Spliced Motif
solvedbrowser + CLI Problem statement Open in the workbench Download .bl
Any longest common subsequence is a valid answer, so the assertion checks the length and that the motif really is a subsequence of both strings.
# Rosalind: LCSQ — Finding a Shared Spliced Motif
# https://rosalind.info/problems/lcsq/
#
# Given: Two DNA strings s and t.
# Return: A longest common subsequence of s and t.
let s = "AACCTTGG"
let t = "ACACTGTGA"
# Standard LCS table, then walk it backwards to rebuild one optimal answer.
# Any longest common subsequence is accepted, so the traceback's tie-breaking
# does not matter.
fn lcs_of(a, b) {
let rows = len(a) + 1
let cols = len(b) + 1
let table = range(0, rows) |> map(|_| range(0, cols) |> map(|_| 0))
let i = 1
while i < rows {
let ai = substr(a, i - 1, 1)
let row = [0]
let j = 1
while j < cols {
let value = if ai == substr(b, j - 1, 1) {
table[i - 1][j - 1] + 1
} else {
max([table[i - 1][j], row[j - 1]])
}
row = push(row, value)
j = j + 1
}
table = set_row(table, i, row)
i = i + 1
}
# Traceback from the bottom-right corner.
let result = ""
let x = len(a)
let y = len(b)
while x > 0 and y > 0 {
if substr(a, x - 1, 1) == substr(b, y - 1, 1) {
result = substr(a, x - 1, 1) ++ result
x = x - 1
y = y - 1
} else {
if table[x - 1][y] >= table[x][y - 1] then x = x - 1 else y = y - 1
}
}
result
}
fn set_row(table, index, row) {
range(0, len(table)) |> map(|i| if i == index then row else table[i])
}
fn is_subsequence(needle, haystack) {
let at = 0
let i = 0
while i < len(haystack) and at < len(needle) {
if substr(haystack, i, 1) == substr(needle, at, 1) then at = at + 1
i = i + 1
}
at == len(needle)
}
let motif = lcs_of(s, t)
println("Result: " + motif + " (length " + str(len(motif)) + ")")
println("Expected: a length-6 common subsequence, such as AACTGG")
fn test_lcsq_shared_spliced_motif() {
assert len(motif) == 6, "LCSQ: length " + str(len(motif)) + " for '" + motif + "'"
assert is_subsequence(motif, s), "LCSQ: '" + motif + "' is not a subsequence of s"
assert is_subsequence(motif, t), "LCSQ: '" + motif + "' is not a subsequence of t"
}
SCSP — Interleaving Two Motifs
solvedbrowser + CLI Problem statement Open in the workbench Download .bl
Any shortest common supersequence is valid, so the assertion checks the length and that both inputs are subsequences of the result.
# Rosalind: SCSP — Interleaving Two Motifs
# https://rosalind.info/problems/scsp/
#
# Given: Two DNA strings s and t.
# Return: A shortest common supersequence of s and t.
let s = "ATCTGAT"
let t = "TGCATA"
fn set_row(grid, index, row) {
range(0, len(grid)) |> map(|i| if i == index then row else grid[i])
}
# The shortest common supersequence is built from the longest common
# subsequence: walk both strings, emitting shared characters once and the rest
# as they come.
fn lcs_table(a, b) {
let grid = range(0, len(a) + 1) |> map(|_| range(0, len(b) + 1) |> map(|_| 0))
let i = 1
while i <= len(a) {
let ai = substr(a, i - 1, 1)
let row = [0]
let j = 1
while j <= len(b) {
let value = if ai == substr(b, j - 1, 1) {
grid[i - 1][j - 1] + 1
} else {
max([grid[i - 1][j], row[j - 1]])
}
row = push(row, value)
j = j + 1
}
grid = set_row(grid, i, row)
i = i + 1
}
grid
}
let grid = lcs_table(s, t)
let result = ""
let x = len(s)
let y = len(t)
while x > 0 and y > 0 {
if substr(s, x - 1, 1) == substr(t, y - 1, 1) {
result = substr(s, x - 1, 1) ++ result
x = x - 1
y = y - 1
} else {
if grid[x - 1][y] >= grid[x][y - 1] {
result = substr(s, x - 1, 1) ++ result
x = x - 1
} else {
result = substr(t, y - 1, 1) ++ result
y = y - 1
}
}
}
result = substr(s, 0, x) ++ substr(t, 0, y) ++ result
fn is_subsequence(needle, haystack) {
let at = 0
let i = 0
while i < len(haystack) and at < len(needle) {
if substr(haystack, i, 1) == substr(needle, at, 1) then at = at + 1
i = i + 1
}
at == len(needle)
}
println("Result: " + result + " (length " + str(len(result)) + ")")
println("Expected: a length-9 supersequence, such as ATGCATGAT")
fn test_scsp_shortest_common_supersequence() {
assert len(result) == 9, "SCSP: length " + str(len(result)) + " for '" + result + "'"
assert is_subsequence(s, result), "SCSP: s is not a subsequence of the result"
assert is_subsequence(t, result), "SCSP: t is not a subsequence of the result"
}