Ответ 1
Попробуйте понять регулярное выражение, построив его. Во-первых, палиндром должен начинаться и заканчиваться одной и той же последовательностью символов в противоположном направлении:
^(.)(.)(.) ... \3\2\1$
мы хотим переписать это так, чтобы за ...
следовала только конечная длина шаблонов, так что мы могли бы преобразовать его в *
. Это возможно с учетом:
^(.)(?=.*\1$)
(.)(?=.*\2\1$)
(.)(?=.*\3\2\1$) ...
но есть еще редкие части. Что делать, если мы можем "записать" ранее захваченные группы? Если возможно, мы могли бы переписать его как:
^(.)(?=.*(?<record>\1\k<record>)$) # \1 = \1 + (empty)
(.)(?=.*(?<record>\2\k<record>)$) # \2\1 = \2 + \1
(.)(?=.*(?<record>\3\k<record>)$) # \3\2\1 = \3 + \2\1
...
который может быть преобразован в
^(?:
(.)(?=.*(\1\2)$)
)*
Почти хорошо, за исключением того, что \2
(записанный захват) сначала не пуст. Он просто ничего не сможет сопоставить. Нам нужно, чтобы он был пустым, если записанный захват не существует. Так выражается условное выражение.
(?(2)\2|) # matches \2 if it exist, empty otherwise.
поэтому наше выражение становится
^(?:
(.)(?=.*(\1(?(2)\2|))$)
)*
Теперь он соответствует первой половине палиндрома. Как насчет второй половины? Ну, после того, как первая половина будет соответствовать, записанный захват \2
будет содержать вторую половину. Поэтому давайте просто положим его в конец.
^(?:
(.)(?=.*(\1(?(2)\2|))$)
)*\2$
Мы также хотим заботиться о палиндроме нечетной длины. Будет свободный символ между 1-й и 2-й половиной.
^(?:
(.)(?=.*(\1(?(2)\2|))$)
)*.?\2$
Это работает хорошо, за исключением одного случая - когда есть только 1 символ. Это опять же из-за \2
ничего не соответствует. Итак,
^(?:
(.)(?=.*(\1(?(2)\2|))$)
)*.?\2?$
# ^ since \2 must be at the end in the look-ahead anyway.