要找到两个字符串A和B的最长公共子序列(LCS),可以像你发布的链接中所示,沿着对角线遍历一个二维数组。数组中的每个元素都对应于查找子串A'和B'(A由其行号切割,B由其列号切割)的LCS的问题。可以通过计算数组中所有元素的值来解决此问题。在计算数组元素的值时,必须确保计算该值所需的所有子问题已经得到解决。这就是为什么要沿着二维数组对角线遍历的原因。
可以将此解决方案扩展到查找N个字符串之间的最长公共子序列,但这需要一种通用的方法来迭代N维数组,以便只有在解决元素所需的所有子问题时才能到达任何元素。
与特定顺序迭代N维数组不同,您还可以递归解决此问题。使用递归时,保存中间解非常重要,因为许多分支将需要相同的中间解。我已经用C#编写了一个小例子来实现这一点:
string lcs(string[] strings)
{
if (strings.Length == 0)
return "";
if (strings.Length == 1)
return strings[0];
int max = -1;
int cacheSize = 1;
for (int i = 0; i < strings.Length; i++)
{
cacheSize *= strings[i].Length;
if (strings[i].Length > max)
max = strings[i].Length;
}
string[] cache = new string[cacheSize];
int[] indexes = new int[strings.Length];
for (int i = 0; i < indexes.Length; i++)
indexes[i] = strings[i].Length - 1;
return lcsBack(strings, indexes, cache);
}
string lcsBack(string[] strings, int[] indexes, string[] cache)
{
for (int i = 0; i < indexes.Length; i++ )
if (indexes[i] == -1)
return "";
bool match = true;
for (int i = 1; i < indexes.Length; i++)
{
if (strings[0][indexes[0]] != strings[i][indexes[i]])
{
match = false;
break;
}
}
if (match)
{
int[] newIndexes = new int[indexes.Length];
for (int i = 0; i < indexes.Length; i++)
newIndexes[i] = indexes[i] - 1;
string result = lcsBack(strings, newIndexes, cache) + strings[0][indexes[0]];
cache[calcCachePos(indexes, strings)] = result;
return result;
}
else
{
string[] subStrings = new string[strings.Length];
for (int i = 0; i < strings.Length; i++)
{
if (indexes[i] <= 0)
subStrings[i] = "";
else
{
int[] newIndexes = new int[indexes.Length];
for (int j = 0; j < indexes.Length; j++)
newIndexes[j] = indexes[j];
newIndexes[i]--;
int cachePos = calcCachePos(newIndexes, strings);
if (cache[cachePos] == null)
subStrings[i] = lcsBack(strings, newIndexes, cache);
else
subStrings[i] = cache[cachePos];
}
}
string longestString = "";
int longestLength = 0;
for (int i = 0; i < subStrings.Length; i++)
{
if (subStrings[i].Length > longestLength)
{
longestString = subStrings[i];
longestLength = longestString.Length;
}
}
cache[calcCachePos(indexes, strings)] = longestString;
return longestString;
}
}
int calcCachePos(int[] indexes, string[] strings)
{
int factor = 1;
int pos = 0;
for (int i = 0; i < indexes.Length; i++)
{
pos += indexes[i] * factor;
factor *= strings[i].Length;
}
return pos;
}
我的示例代码可以进一步优化。许多被缓存的字符串是重复的,有些只是添加了一个额外的字符而已。当输入字符串变大时,这会占用更多的空间。
对于输入:"666222054263314443712"、"5432127413542377777" 和 "6664664565464057425"
LCS 返回的是 "54442"。
abc12
、12abc
、12xyz
错误的结果。 - sth('abacbdab', 'bdcaba')
,至少存在五个最长公共子序列:"baa", "bab", "bca", "bcb", "dab"
。您的代码是否考虑到了这一点? - ypercubeᵀᴹ