I wrote a program to find the longest common subsequence among several strings. I used a naive algorithm and implementation.
The motivation was solving the Rosalind problem at http://rosalind.info/problems/lcs/ . You can find sample input there as well. The Rosalind problem concerns strings as DNA, but I think my code can be treated as a general string operation.
The problem asks for any of the common substrings if there is more than one, but I find all of them.
How can this code be improved? What obvious problems are there?
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Finding_a_Shared_Motif
{
class Program
{
private static void Main()
{
var input = File.ReadAllLines("rosalind_lcs.txt").ToList();
var t = new Stopwatch();
t.Restart();
var lcs = LongestCommonSubstring(input);
t.Stop();
File.WriteAllLines("output.txt", lcs);
Console.WriteLine("Finished in {0} msec.", t.ElapsedMilliseconds);
Console.ReadLine();
}
public static IEnumerable<string> LongestCommonSubstring(List<string> strings)
{
var lcs = LongestCommonSubstring(strings[0], strings[1]);
for (var i = 2; i < strings.Count(); i++)
{
var new_lcs = new BestStrings();
foreach (var s in lcs) new_lcs.Add(LongestCommonSubstring(s, strings[i]));
lcs = new_lcs;
}
return lcs;
}
private static BestStrings LongestCommonSubstring(string s1, string s2)
{
var lcs = new BestStrings();
for (var i = 1 - s2.Length; i < s1.Length; i++)
{
var substrings = BestSubstringWithAlignment(s1, s2, i);
if (substrings.Length == 0) continue;
lcs.Add(substrings);
}
return lcs;
}
private static BestStrings BestSubstringWithAlignment(string s1, string s2, int offset)
{
var substrings = new BestStrings();
var substring = "";
for (var i = Math.Max(0, offset); i < s1.Length && i < s2.Length + offset; i++)
{
var c1 = s1[i];
var c2 = s2[i - offset];
if (c1 == c2)
{
substring = substring + c1;
}
else
{
substrings.Add(substring);
substring = "";
}
}
substrings.Add(substring);
return substrings;
}
sealed class BestStrings : Collection<string>
{
public int Length
{
get { return base[0].Length; }
}
public BestStrings()
{
base.Add("");
}
public new void Add(string s)
{
if (s.Length == 0 || s.Length < Length || Contains(s)) return;
if (s.Length > Length) Clear();
base.Add(s);
}
public void Add(IEnumerable<string> collection)
{
foreach (var s in collection) Add(s);
}
}
}
}