Алгоритм «Самая длинная общая подпоследовательность» не требует, чтобы совпадения были смежными подстроками. Например, для строк «компьютер» и «плавучий дом» подпоследовательность «вне». Это то, что вы хотите?
Если вы хотите, чтобы совпадения были смежными подстроками, это называется самой длинной повторяющейся проблемой подстрок . Ссылка описывает линейный временной и пространственный алгоритм с использованием суффиксных деревьев.
Если вы хотите что-то короткое и простое, вот подход, основанный на алгоритме LCS, но без таблицы. Идея состоит в том, чтобы зациклить все возможные целочисленные сдвиги между желаемой подстрокой и ее дубликатом. Для каждой смены найдите наибольшее непрерывное совпадение, отсканировав строку один раз. Если входная строка имеет длину n, существует O (n) возможных сдвигов, и проверка каждого сдвига занимает O (n) времени, поэтому общая стоимость составляет O (n ^ 2) с постоянным количеством места. (Сравните с моим простым словарным ответом, который занимает O (n ^ 3) времени и O (n ^ 2) пробела.) Если вы не хотите, чтобы совпадающие совпадения (т.е. вы хотите, чтобы «bbbb» возвращал «bb», а не «bbb») ), то при проверке каждой смены вы останавливаетесь, если наибольшее совпадение превышает смену. Вот код C #:
public static string FindDuplicateSubstring(string s,
bool allowOverlap = false)
{
int matchPos = 0, maxLength = 0;
for (int shift = 1; shift < s.Length; shift++) {
int matchCount = 0;
for (int i = 0; i < s.Length - shift; i++) {
if (s[i] == s[i+shift]) {
matchCount++;
if (matchCount > maxLength) {
maxLength = matchCount;
matchPos = i-matchCount+1;
}
if (!allowOverlap && (matchCount == shift)) {
// we have found the largest allowable match
// for this shift.
break;
}
} else matchCount = 0;
}
}
if (maxLength > 0) return s.Substring(matchPos, maxLength);
else return null;
}
Я проверил это по своему словарному ответу, и он дает те же результаты. Но для случайной строки длиной 3000 словарю требуется 15 секунд, в то время как описанный выше метод занимает 60 мс (и намного меньше памяти).