Solution 1.1: Brute force
- Consider all possible sequences of one string
- For each of these sequences, check if it's a subsequence in the other string
- If it is, note its length if it's longer than the last longest subsequence
def longest_common_subsequence(s1, s2)
m, n = s1.length, s2.length
longest_length = 0
(1 .. 2**m-1).each do |bitmask|
subsequence = []
i = 0
while 2**i <= bitmask
if bitmask & 2**i != 0
subsequence << s1[i]
end
i += 1
end
i = 0
s2.each_char do |c|
if subsequence[i] == c
i += 1
end
end
if i == subsequence.length && i > longest_length
longest_length = i
end
end
longest_length
end
Time |
O(n * 2m) |
m comparisons for each of 2n possible subsequences |
Space |
O(1) |
The length of the longest subsequence seen |
Solution 1.2: Dynamic programming
- Compare every prefix of one string to the other
- If the last characters of each prefix match, add 1 to the longest length
without the last characters
- Otherwise, the longest length is the last longest length
def longest_common_subsequence(s1, s2)
m, n = s1.length, s2.length
lcs_lengths = Array.new(m + 1) { Array.new(n + 1, 0) }
(1 .. m).each do |i|
(1 .. n).each do |j|
if s1[i-1] == s2[j-1]
lcs_lengths[i][j] = 1 + lcs_lengths[i-1][j-1]
else
lcs_lengths[i][j] = [
lcs_lengths[i-1][j],
lcs_lengths[i][j-1]
].max
end
end
end
lcs_lengths[m][n]
end
Time |
O(mn) |
Two loops of m and n time |
Space |
O(mn) |
2D array of longest common subsequence lengths |