Учитывая слово, преобразуйте его в палиндром с минимальным добавлением к нему букв

Вот довольно интересный вопрос интервью:

Учитывая слово, добавьте наименьшее количество букв к нему, чтобы преобразовать его в палиндром.

Например, если "hello" - это строка, результат должен быть "hellolleh". Если задан "кокос", результатом должен быть "cococ".

Один из подходов, к которому я могу придумать, - добавить конец строки в конец исходной строки, а затем попытаться устранить лишние символы с конца. Однако я не могу понять, как это сделать эффективно. У кого-нибудь есть идеи?

Ответ 1

Сначала создайте функцию для проверки строки для palindrome-ness, имея в виду, что "a" и "aa" являются палиндромами. Они палиндромы, правильно???

Если вход является палиндром, верните его (необходимо добавить 0 символов) Loop from x [length] down to x [1] проверяет, является ли подмножество строки x [i].. x [length] палиндром, чтобы найти самый длинный палиндром.

Возьмите подстроку из входной строки до самого длинного палиндрома, переверните ее и добавьте в конец, чтобы сделать кратчайший палиндром через добавление.

coco = > c + oco = > c + oco + c

mmmeep = > mmmee + p = > mmmee + p + eemmm

Ответ 2

Хорошо! Вот моя вторая попытка.

Идея состоит в том, что мы хотим найти, сколько символов в конце строки можно повторно использовать при добавлении дополнительных символов для завершения палиндрома. Для этого мы будем использовать модификацию алгоритма соответствия строк KMP. Используя KMP, мы ищем исходную строку для ее обратного. Как только мы дойдем до самого конца строки, у нас будет как можно больше совпадений между обратным знаком строки и исходной строкой, которая встречается в конце строки. Например:

HELLO
    O

1010
 010

3202
 202

1001
1001

В этот момент KMP обычно скажет "нет совпадения", если исходная строка не была палиндром. Однако, поскольку мы в настоящее время знаем, сколько ссылок на обратную строку было сопоставлено, мы можем просто выяснить, сколько символов все еще отсутствует, а затем привязать их к концу строки. В первом случае нам не хватает LLEH. Во втором случае нам не хватает 1. В-третьих, нам не хватает 3. В последнем случае мы ничего не теряем, так как исходная строка является палиндром.

Время выполнения этого алгоритма - это время выполнения стандартного поиска KMP плюс время, необходимое для изменения строки: O (n) + O (n) = O (n).

Итак, теперь, чтобы утверждать правильность. Это потребует определенных усилий. Рассмотрим оптимальный ответ:

   | original string | | extra characters |

Предположим, что мы читаем это назад от конца, а это значит, что мы будем читать хотя бы обратную сторону исходной строки. Часть этой перевернутой строки продолжается назад в тело самой исходной строки. Фактически, чтобы свести к минимуму количество добавленных символов, это должно быть максимально возможное количество символов, которое заканчивается в самой строке. Мы можем видеть это здесь:

   | original string | | extra characters |
           | overlap |

Теперь, что происходит на нашем этапе KMP? Ну, когда вы ищете обратную строку внутри себя, KMP будет поддерживать как можно больше совпадения во все времена, поскольку он работает через строку. Это означает, что когда KMP попадает в конец строки, согласованная часть, которую он поддерживает, будет самым длинным возможным совпадением, поскольку KMP только перемещает начальную точку совпадения кандидатов вперед при сбое. Следовательно, у нас есть это максимально возможное перекрытие, поэтому мы получим кратчайшее количество символов, необходимое в конце.

Я не уверен на 100%, что это работает, но похоже, что это работает в каждом случае, я могу бросить на него. Доказательство правильности кажется разумным, но оно немного волнообразно, потому что формальное доказательство на основе KMP, вероятно, будет немного сложным.

Надеюсь, это поможет!

Ответ 3

Чтобы ответить, я бы взял этот наивный подход:

  • когда нам нужны 0 символов? когда строка это палиндром
  • когда нам нужен 1 символ? если кроме первой символьной строки находится палиндром
  • когда нам нужны 2 символа? если кроме двух стартовых символов строка является палиндром
  • и т.д....

Таким образом, алгоритм может быть

  for index from 1 to length
   if string.right(index) is palindrome
    return string + reverse(string.left(index))
   end
  next

изменить

Я не очень парень на Python, но простая реализация приведенного выше псевдокода может быть

>>> def rev(s): return s[::-1]
... 
>>> def pal(s): return s==rev(s)
... 
>>> def mpal(s):
...  for i in range(0,len(s)):
...   if pal(s[i:]): return s+rev(s[:i])
... 
>>> mpal("cdefedcba")
'cdefedcbabcdefedc'
>>> pal(mpal("cdefedcba"))
True

Ответ 4

Простое линейное решение времени.

Позвоните в нашу строку S.

Пусть f (X, P) - длина самого длинного общего префикса X и P. Вычисление f (S [0], rev (S)), f (S [1], rev (S)),... где S [k] - суффикс S, начинающийся с позиции k. Очевидно, вы хотите выбрать минимум k такой, что k + f (S [k], rev (S)) = len (S). Это означает, что вам просто нужно добавить k символов в конце. Если k равно 0, укус уже является палиндром. Если k = len (S), то вам нужно добавить все обратное.

Нам нужно быстро вычислить f (S [i], P) для всех S [i]. Это сложная часть. Создайте дерево суффиксов S. Проведите дерево и обновите каждую node длиной самого длинного общего префикса с P. Значения в листьях соответствуют f (S [i], P).