Я пытаюсь решить Самая длинная палиндромная подстрока в Leetcode.Мне известны решения этой проблемы, такие как расширение вокруг центра или динамическое программирование снизу вверх .В чисто образовательных целях я хотел решить эту проблему рекурсивным способом.Я пытаюсь найти решение, подобное тому, что описано здесь или здесь .(проблема немного в другом).У меня есть эта функция:
private (int Start, int End) Longest(string s, int i, int j)
, которая принимает строку + начальную и конечную позицию поиска.Возвращение кортежа - начало и конец самого длинного палиндрома.Я пытаюсь разделить на следующие случаи:
- , если s [i] == s [j] исследовать Дольше всего (s, i + 1, j-1)
- Исследовать дольше(s, i + 1, j)
- Исследовать самое длинное (s, i, j - 1)
- Возвращать самое длинное (максимальное различие между возвращенным началом и концом) из этих трех
Конечно, я использую словарь с кортежем (int, int) в качестве ключа (значения i и j), чтобы запомнить все вычисленные результаты, чтобы не вычислять их снова.
Полный код приведен ниже, но оночень грязно после нескольких итераций, когда я пытался исправить алгоритм.Я полагаю, что конкретный код не очень важен.
Код, похоже, возвращает правильные результаты, но в Leetcode превышен лимит времени.Есть ли правильное быстрое рекурсивное решение?Я полагаю, что должно быть так, как существует DP восходящее решение.
код:
private readonly IDictionary<(int, int), (int, int)> _mem = new Dictionary<(int, int), (int, int)>();
private (int Start, int End) Longest(string s, int i, int j) {
if (i >= j) {
return (i, j);
}
if (_mem.TryGetValue((i, j), out var ret)) {
return ret;
}
var newI = i + 1;
var newJ = j - 1;
ValueTuple<int, int> removingTwo;
if (s[i] == s[j])
{
removingTwo = Longest(s, newI, newJ);
if (removingTwo.Item1 == newI && removingTwo.Item2 == newJ) {
removingTwo.Item1--;
removingTwo.Item2++;
}
}
else {
removingTwo = (1, 0);
}
var removingFirst = Longest(s, newI, j);
var removingLast = Longest(s, i, newJ);
var mT = removingTwo.Item2 - removingTwo.Item1;
var mF = removingFirst.End - removingFirst.Start;
var mL = removingLast.End - removingLast.Start;
var max = Math.Max(mT, mF);
max = Math.Max(max, mL);
ValueTuple<int, int> retVal;
if (max == mT) retVal = removingTwo;
else if (max == mF) retVal = removingFirst;
else retVal = removingLast;
_mem.Add((i, j), retVal);
return retVal;
}
Редактировать : рабочее восходящее решение (скопировано из geegsforgeegs ):
public string LongestPalindrome(string s) {
if (s.Length == 0) return "";
var table = new bool[s.Length, s.Length];
var len = s.Length;
for (int i = 0; i < len; i++) {
table[i,i] = true;
}
var start = 0;
var max = 1;
for (int i = 0; i < len - 1; i++) {
if (s[i] == s[i + 1]) {
start = i;
max = 2;
table[i, i+1] = true;
}
}
for (int k = 3; k <= len; ++k) {
// Fix the starting index
for (int i = 0; i < len - k + 1; ++i)
{
// Get the ending index of substring from
// starting index i and length k
int j = i + k - 1;
// checking for sub-string from ith index to
// jth index iff str.charAt(i+1) to
// str.charAt(j-1) is a palindrome
if (table[i + 1, j - 1] && s[i] == s[j]) {
table[i,j] = true;
if (k > max) {
start = i;
max = k;
}
}
}
}
return s.Substring(start, max);
}