#algorithm #dynamic-programming
#алгоритм #динамическое программирование
Вопрос:
Я реализовал решение динамического программирования, чтобы найти самую длинную общую подпоследовательность среди 2 строк. По-видимому, есть способ обобщить этот алгоритм, чтобы найти LCS среди 3 строк, но в моих исследованиях я не нашел никакой информации о том, как это сделать. Любая помощь будет оценена.
Комментарии:
1. Изменение его на работу для 3 строк вместо 2 на самом деле не является «обобщением». Если вы обобщаете, то он должен работать для любого количества строк.
2. Ах. В этом случае мне нужно, чтобы он работал для 3 строк, не обязательно любого числа.
Ответ №1:
Чтобы найти самую длинную общую подпоследовательность (LCS) из 2 строк A и B, вы можете пройти 2-мерный массив по диагонали, как показано в опубликованной вами ссылке. Каждый элемент в массиве соответствует задаче нахождения LCS подстрок A’ и B’ (A сокращается по номеру строки, B сокращается по номеру столбца). Эта проблема может быть решена путем вычисления значения всех элементов в массиве. Вы должны быть уверены, что при вычислении значения элемента массива все подзадачи, необходимые для вычисления этого заданного значения, уже решены. Вот почему вы проходите 2-мерный массив по диагонали.
Это решение можно масштабировать для нахождения самой длинной общей подпоследовательности между 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)] = resu<
return resu<
}
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»