Как я могу вычислить количество символов, необходимых для превращения строки в палиндром?
недавно я нашел проблему конкурса, которая просит вас вычислить минимальное количество символов, которые должны быть вставлены (в любом месте) в строку, чтобы превратить ее в палиндром.
например, учитывая строку: "abcbd", мы можем превратить ее в палиндром, вставив только два символа: один после " a "и другой после" d": "adbcbda".
Это, похоже, обобщение аналогичной проблемы, которая требует того же самого, за исключением символы могут быть добавлены только в конце - это довольно простое решение в O (N) с использованием хэш-таблиц.
Я пытался изменить Левенштейна алгоритм чтобы решить эту проблему, но не увенчались успехом. Любая помощь в том, как решить эту проблему (она не обязательно должна быть эффективной, меня просто интересует любое решение DP), будет оценена.
3 ответов
Примечание: это просто любопытство. Дав предложил алгоритм, который может быть изменен на алгоритм DP, чтобы работать в O(n^2) времени и O(n^2) пространстве легко (и, возможно, O(n) с лучшей бухгалтерией).
конечно, этот "наивный" алгоритм может пригодиться, если вы решите изменить разрешенные операции.
вот "наивный" алгоритм, который, вероятно, можно сделать быстрее с помощью умной бухгалтерии.
Дана строка, мы догадываемся середине полученный палиндром, а затем попробуйте вычислить количество вставок, необходимых для того, чтобы сделать строку палиндромом вокруг этой середины.
если строка имеет длину n, существует 2n+1 возможных средних (каждый символ, между двумя символами, непосредственно перед и сразу после строки).
предположим, что мы рассмотрим середину, которая дает нам две строки L и R (одна слева и одна справа).
Если мы используем вставки, я считаю Самое Длинное Общее Подпоследовательность алгоритм (который является алгоритмом DP) теперь можно использовать для создания "супер" строки, которая содержит как L, так и обратный R, см. самый короткий общий supersequence.
выберите середину, которая дает вам наименьшее число вставок.
Это O (n^3) я считаю. (Примечание: Я не пытался доказать, что это правда).
мое решение C# ищет повторяющиеся символы в строке и использует их для уменьшения количества вставок. Одним словом,программа, Я использую символы " r " в качестве границы. Внутри " r " я делаю это палиндромом (рекурсивно). За пределами " р " я отражаю символы слева и справа.
некоторые входные сигналы имеют больше чем один самый короткий выход:выход может быть toutptuot или outuputuo. Мое решение выбирает только одну из возможностей.
какой-то пример работает:
- радар ->радар, 0 вставки
- esystem ->metsystem, 2 вставки
- ->megassagem, 3 вставки
- stackexchange ->stegnahckexekchangets, 8 вставки
сначала мне нужно проверить, если вход уже палиндром:
public static bool IsPalindrome(string str)
{
for (int left = 0, right = str.Length - 1; left < right; left++, right--)
{
if (str[left] != str[right])
return false;
}
return true;
}
затем мне нужно найти любые повторяющиеся символы во входных данных. Их может быть несколько. Слово имеет два наиболее повторяющихся символа ('e' и 's'):
private static bool TryFindMostRepeatedChar(string str, out List<char> chs)
{
chs = new List<char>();
int maxCount = 1;
var dict = new Dictionary<char, int>();
foreach (var item in str)
{
int temp;
if (dict.TryGetValue(item, out temp))
{
dict[item] = temp + 1;
maxCount = temp + 1;
}
else
dict.Add(item, 1);
}
foreach (var item in dict)
{
if (item.Value == maxCount)
chs.Add(item.Key);
}
return maxCount > 1;
}
мой алгоритм здесь:
public static string MakePalindrome(string str)
{
List<char> repeatedList;
if (string.IsNullOrWhiteSpace(str) || IsPalindrome(str))
{
return str;
}
//If an input has repeated characters,
// use them to reduce the number of insertions
else if (TryFindMostRepeatedChar(str, out repeatedList))
{
string shortestResult = null;
foreach (var ch in repeatedList) //"program" -> { 'r' }
{
//find boundaries
int iLeft = str.IndexOf(ch); // "program" -> 1
int iRight = str.LastIndexOf(ch); // "program" -> 4
//make a palindrome of the inside chars
string inside = str.Substring(iLeft + 1, iRight - iLeft - 1); // "program" -> "og"
string insidePal = MakePalindrome(inside); // "og" -> "ogo"
string right = str.Substring(iRight + 1); // "program" -> "am"
string rightRev = Reverse(right); // "program" -> "ma"
string left = str.Substring(0, iLeft); // "program" -> "p"
string leftRev = Reverse(left); // "p" -> "p"
//Shave off extra chars in rightRev and leftRev
// When input = "message", this loop converts "meegassageem" to "megassagem",
// ("ee" to "e"), as long as the extra 'e' is an inserted char
while (left.Length > 0 && rightRev.Length > 0 &&
left[left.Length - 1] == rightRev[0])
{
rightRev = rightRev.Substring(1);
leftRev = leftRev.Substring(1);
}
//piece together the result
string result = left + rightRev + ch + insidePal + ch + right + leftRev;
//find the shortest result for inputs that have multiple repeated characters
if (shortestResult == null || result.Length < shortestResult.Length)
shortestResult = result;
}
return shortestResult;
}
else
{
//For inputs that have no repeated characters,
// just mirror the characters using the last character as the pivot.
for (int i = str.Length - 2; i >= 0; i--)
{
str += str[i];
}
return str;
}
}
обратите внимание, что вам нужна обратная функция:
public static string Reverse(string str)
{
string result = "";
for (int i = str.Length - 1; i >= 0; i--)
{
result += str[i];
}
return result;
}
C# рекурсивное решение добавление в конец строки:
есть 2 базовых случая. Когда длина 1 или 2. Рекурсивный случай: если экстремумы равны, то сделайте палиндром внутренней строкой без крайностей и верните ее с крайностями. Если экстремумы не равны, добавьте первый символ в конец и сделайте палиндром внутренняя строка, включая предыдущий последний символ. верни это.
public static string ConvertToPalindrome(string str) // By only adding characters at the end
{
if (str.Length == 1) return str; // base case 1
if (str.Length == 2 && str[0] == str[1]) return str; // base case 2
else
{
if (str[0] == str[str.Length - 1]) // keep the extremes and call
return str[0] + ConvertToPalindrome(str.Substring(1, str.Length - 2)) + str[str.Length - 1];
else //Add the first character at the end and call
return str[0] + ConvertToPalindrome(str.Substring(1, str.Length - 1)) + str[0];
}
}