これは、すべての最長共通部分列の差分を返すバージョンです(基本的に、キャッシュされたテーブルを使用したバックトラック-すべての最長共通部分列のすべての最長共通部分列に到達するために採用されたアプローチと同様です)(または、私のブログを参照してください@ :http ://codingworkout.blogspot.com/2014/07/longest-common-subsequence.html )
たとえば、GACとAGCATの場合、次のようになります=> {{"[G] [A] C"、 "[G] A [C]"、 "G [A] [C]"}、{"A [G ] C [A] T "、" A [G] [C] AT "、" [A] G [C] AT "}ここで、GA、GC、およびACは最長共通部分列です。
string[][] GetDiffs(string A, string B, int aIndex, int bIndex,
int[][] DP_LCS_AllPrefixes_Cache)
{
if((aIndex == 0) && (bIndex ==0))
{
return null;
}
if (DP_LCS_AllPrefixes_Cache[aIndex][bIndex] == 0)
{
var r = new string[2][];
r[0] = new string[] { A.Substring(0, aIndex) };
r[1] = new string[] { B.Substring(0, bIndex) };
return r;
}
if (A[aIndex - 1] == B[bIndex - 1])
{
var r = this.GetDiffs(A, B, aIndex - 1, bIndex - 1,
DP_LCS_AllPrefixes_Cache);
string ch = string.Format("[{0}]", A[aIndex - 1]);
if (r == null)
{
r = new string[2][];
r[0] = new string[] { ch };
r[1] = new string[] { ch };
}
else
{
r[0] = r[0].Select(s => s + ch).ToArray();
r[1] = r[1].Select(s => s + ch).ToArray();
}
return r;
}
int lcs_up_direction = DP_LCS_AllPrefixes_Cache[aIndex - 1][bIndex];
int lcs_left_direction = DP_LCS_AllPrefixes_Cache[aIndex][bIndex - 1];
string[][] lcs_up = null, lcs_left = null;
if (lcs_up_direction == lcs_left_direction)
{
lcs_up = this.GetDiffs(A, B, aIndex - 1, bIndex,
DP_LCS_AllPrefixes_Cache);
lcs_left = this.GetDiffs(A, B, aIndex, bIndex - 1,
DP_LCS_AllPrefixes_Cache);
}
else if (lcs_up_direction > lcs_left_direction)
{
lcs_up = this.GetDiffs(A, B, aIndex - 1, bIndex,
DP_LCS_AllPrefixes_Cache);
}
else
{
lcs_left = this.GetDiffs(A, B, aIndex, bIndex - 1, DP_LCS_AllPrefixes_Cache);
}
char a = A[aIndex - 1], b = B[bIndex - 1];
string[][] rl = new string[2][];
rl[0] = new string[0];
rl[1] = new string[0];
if(lcs_up != null)
{
//we moved upward, that is we accepted that they differ with 'A' at aIndex-1 (a)
rl[0] = lcs_up[0].Select(s => s + a.ToString()).ToArray();
rl[1] = lcs_up[1];
}
if (lcs_left != null)
{
//we moved left, that is we accepted that they differ with 'B' at bIndex-1 (b)
rl[0] = rl[0].Union(lcs_left[0]).ToArray(); ;
rl[1] = rl[1].Union(lcs_left[1].Select(s => s + b.ToString())).ToArray();
}
return rl.ToArray();
}
発信者はどこにいますか
string[][] GetDiffs(string A, string B, int[][] DP_LCS_AllPrefixes_Cache)
{
var r = this.GetDiffs(A, B, A.Length, B.Length,
DP_LCS_AllPrefixes_Cache);
return r;
}
そして、バックトラックするためにLCSの長さをキャプチャするDPメソッド
public int[][] LCS_OfAllPrefixes_Length(string A, string B)
{
A.ThrowIfNullOrWhiteSpace("a");
B.ThrowIfNullOrWhiteSpace("b");
int[][] DP_LCS_AllPrefixes_Cache = new int[A.Length+1][];
for(int i = 0;i<DP_LCS_AllPrefixes_Cache.Length; i++)
{
DP_LCS_AllPrefixes_Cache[i] = new int[B.Length + 1];
}
for (int rowIndexOfCache = 1; rowIndexOfCache <= A.Length; rowIndexOfCache++)
{
for (int columnIndexOfCache = 1; columnIndexOfCache <= B.Length; columnIndexOfCache++)
{
//LCS(Ai, Bj) = 0 if i <=0, or j <= 0
// LCS(Ai, Bj) + 1 if Ai == Bj
// Max(LCS(Ai-1, Bj), LCS(Ai, Bj-1))
if(A[rowIndexOfCache-1] == B[columnIndexOfCache-1])
{
DP_LCS_AllPrefixes_Cache[rowIndexOfCache][columnIndexOfCache] = DP_LCS_AllPrefixes_Cache[rowIndexOfCache - 1][columnIndexOfCache - 1] + 1;
}
else
{
DP_LCS_AllPrefixes_Cache[rowIndexOfCache][columnIndexOfCache] = Utilities.Max(DP_LCS_AllPrefixes_Cache[rowIndexOfCache - 1][columnIndexOfCache],
DP_LCS_AllPrefixes_Cache[rowIndexOfCache][columnIndexOfCache - 1]);
}
}
}
return DP_LCS_AllPrefixes_Cache;
}
試験方法
[TestMethod]
public void LCS_Tests()
{
string A = "GAC", B = "AGCAT";
var DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 2);
var lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(lcs_sequences);
var diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == lcs_sequences.Length);
Assert.IsTrue(diffs[1].Length == lcs_sequences.Length);
Assert.IsTrue(lcs_sequences.Any(s => "AC".Equals(s)));
Assert.IsTrue(lcs_sequences.Any(s => "GC".Equals(s)));
Assert.IsTrue(lcs_sequences.Any(s => "GA".Equals(s)));
var DP_LCS_AllPrefixes_Subsequences_Cache = this.LCS_OfAllPrefixes_Subsequences(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Length == 2);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "AC".Equals(s)));
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "GC".Equals(s)));
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "GA".Equals(s)));
A = "ABCDGH"; B = "AEDFHR";
DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 3);
lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(lcs_sequences);
diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == lcs_sequences.Length);
Assert.IsTrue(diffs[1].Length == lcs_sequences.Length);
Assert.IsTrue(lcs_sequences.Any(s => "ADH".Equals(s)));
DP_LCS_AllPrefixes_Subsequences_Cache = this.LCS_OfAllPrefixes_Subsequences(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Length == 3);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "ADH".Equals(s)));
A = "AGGTAB"; B = "GXTXAYB";
DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 4);
lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(lcs_sequences);
diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == 2);
Assert.IsTrue(diffs[1].Length == lcs_sequences.Length);
Assert.IsTrue(lcs_sequences.Any(s => "GTAB".Equals(s)));
DP_LCS_AllPrefixes_Subsequences_Cache = this.LCS_OfAllPrefixes_Subsequences(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Length == 4);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "GTAB".Equals(s)));
A = "ABCDEF"; B = "UVWXYZ";
DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 0);
lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == 1);
Assert.IsTrue(diffs[1].Length == 1);
}