it-swarm.com.ru

Как проверить, что строка является палиндромом с помощью регулярных выражений?

Это был вопрос интервью, на который я не смог ответить:

Как проверить, что строка является палиндромом с помощью регулярных выражений?

постскриптум Уже есть вопрос " Как проверить, является ли данная строка палиндромом? ", и она дает много ответов на разных языках, но нет ответа, который использует регулярные выражения.

71
Degvik

Ответ на этот вопрос заключается в том, что «это невозможно». В частности, интервьюер задается вопросом, обратили ли вы внимание на уроке теории вычислений.

В вашем классе теории вычислений вы узнали о конечных автоматах. Конечный автомат состоит из узлов и ребер. Каждое ребро снабжено буквой из конечного алфавита. Один или несколько узлов являются специальными «принимающими» узлами, а один узел является «начальным» узлом. Когда каждая буква читается из данного Слова, мы пересекаем данный Край в машине. Если мы оказываемся в состоянии принятия, то мы говорим, что машина «принимает» это Слово.

Регулярное выражение всегда можно перевести в эквивалентный конечный автомат. То есть тот, который принимает и отклоняет те же слова, что и регулярное выражение (в реальном мире некоторые языки регулярного выражения допускают произвольные функции, они не учитываются).

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

a ^ x b a ^ x (например, aba, aabaa, aaabaaa, aaaabaaaa, ....)

где ^ х повторяется х раз. Это требует как минимум x узлов, потому что после просмотра 'b' мы должны отсчитать x раз, чтобы убедиться, что это палиндром.

Наконец, возвращаясь к исходному вопросу, вы можете сказать интервьюеру, что вы можете написать регулярное выражение, которое принимает все палиндромы, которые меньше, чем некоторая конечная фиксированная длина. Если существует какое-либо реальное приложение, которое требует идентификации палиндромов, то оно почти наверняка не будет включать произвольно длинные, поэтому этот ответ покажет, что вы можете отличить теоретические невозможности от реальных приложений. Тем не менее, фактическое регулярное выражение будет довольно длинным, намного длиннее, чем эквивалентная четырехстрочная программа (простое упражнение для читателя: напишите программу, которая идентифицирует палиндромы).

123
Jose M Vidal

Хотя механизм PCRE поддерживает рекурсивные регулярные выражения (см. ответ Питера Краусса ), вы не можете использовать регулярное выражение в ICU движке (как, например, Apple), чтобы достичь этого без лишнего кода. Вам нужно будет сделать что-то вроде этого:

Это обнаруживает любой палиндром, но действительно требует цикла (который будет необходим, потому что регулярные выражения не могут считать). 

$a = "teststring";
while(length $a > 1)
{
   $a =~ /(.)(.*)(.)/;
   die "Not a palindrome: $a" unless $1 eq $3;
   $a = $2;
}
print "Palindrome";
42
Airsource Ltd

Это невозможно. Палиндромы не определяются обычным языком. (Смотрите, я DID изучаю что-то в теории вычислений)

27
ZCHudson

С регулярным выражением Perl:

/^((.)(?1)\2|.?)$/

Хотя, как отмечали многие, это нельзя считать регулярным выражением, если вы хотите быть строгим. Регулярные выражения не поддерживает рекурсию.

23
Markus Jarderot

Вот один, чтобы обнаружить 4-буквенный палиндром (например, поступок), для любого типа символа:

\(.\)\(.\)\2\1

Вот один, чтобы обнаружить 5-буквенные палиндромы (например, радар), проверяя только буквы:

\([a-z]\)\([a-z]\)[a-z]\2\1

Так что, похоже, нам нужно различное регулярное выражение для каждой возможной длины Word . Этот пост в списке рассылки Python содержит некоторые детали относительно того, почему (конечные автоматы и лемма прокачки).

11
FOR

Да, вы можете сделать это в .Net!

(?<N>.)+.?(?<-N>\k<N>)+(?(N)(?!))

Вы можете проверить это здесь ! Это замечательный пост!

10
kev

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

Я бы не стал делать это с обычным .__ выражение. Это не подходит использование регулярных выражений.

9
Jon Skeet

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

(.?)(.?)(.?)(.?)(.?).?\5\4\3\2\1
7
Stewart

StackOverflow полон ответов типа «Регулярные выражения? Нет, они не поддерживают его. Они не могут поддерживают его».

Правда в том, что регулярные выражения больше не имеют ничего общего с регулярные грамматики. Современные регулярные выражения имеют функции, такие как группы рекурсии и балансировки, и доступность их реализаций постоянно растет (см., Например, примеры Ruby здесь). На мой взгляд, держаться за старое убеждение, что регулярные выражения в нашей области - это что-то, кроме концепции программирования, просто контрпродуктивно. Вместо того, чтобы ненавидеть их за выбор Слова, который больше не является наиболее подходящим, нам пора принять вещи и двигаться дальше.

Вот цитата Ларри Уолла , создателя самого Perl:

(…) Как правило, имеющие отношение к тому, что мы называем «регулярными выражениями», которые лишь незначительно связаны с реальными регулярными выражениями. Тем не менее, этот термин вырос с возможностями наших механизмов сопоставления с образцом, поэтому я не буду пытаться бороться с лингвистической необходимостью здесь. Однако я обычно буду называть их «регулярными выражениями» (или «регулярными выражениями», когда я нахожусь в англосаксонском настроении).

А вот сообщение в блоге by один из основных разработчиков PHP :

Поскольку статья была довольно длинной, вот краткое изложение основных моментов:

  • «Регулярные выражения», используемые программистами, имеют очень мало общего с исходным понятием регулярности в контексте теории формального языка.
  • Регулярные выражения (по крайней мере, PCRE) могут соответствовать всем контекстно-свободным языкам. Как таковые они могут также соответствовать правильно сформированному HTML и почти всем другим языкам программирования.
  • Регулярные выражения могут соответствовать по крайней мере некоторым контекстно-зависимым языкам.
  • Сопоставление регулярных выражений является NP-полным. Таким образом, вы можете решить любую другую проблему NP, используя регулярные выражения.

При этом, вы можете сопоставить палиндромы с регулярными выражениями, используя это:

^(?'letter'[a-z])+[a-z]?(?:\k'letter'(?'-letter'))+(?(letter)(?!))$

... что, очевидно, не имеет ничего общего с обычными грамматиками.
Подробнее здесь: http://www.regular-expressions.info/balancing.html

7
rr-

Теперь это можно сделать в Perl. Используя рекурсивную ссылку:

if($istr =~ /^((\w)(?1)\g{-1}|\w?)$/){
    print $istr," is palindrome\n";
}

изменено на основе последней части http://perldoc.Perl.org/perlretut.html

4
Hui Liu

В Ruby вы можете использовать именованные группы захвата. так что-то вроде этого будет работать -

def palindrome?(string)
  $1 if string =~ /\A(?<p>| \w | (?: (?<l>\w) \g<p> \k<l+0> ))\z/x
end

попробуй, все работает ...

1.9.2p290 :017 > palindrome?("racecar")
 => "racecar" 
1.9.2p290 :018 > palindrome?("kayak")
 => "kayak" 
1.9.2p290 :019 > palindrome?("woahitworks!")
 => nil 
4
Taylor
/\A(?<a>|.|(?:(?<b>.)\g<a>\k<b+0>))\z/

это действительно для движка Oniguruma (который используется в Ruby)

взял с Прагматичный Книжная полка

3
mpugach

Рекурсивные регулярные выражения могут сделать это!

Итак, простой и самоочевидный алгоритм обнаружения строки, содержащей палиндром:

   (\w)(?:(?R)|\w?)\1

На rexegg.com/regex-recursion учебник объясняет, как это работает.


Он прекрасно работает с любым языком, вот пример, адаптированный из того же источника (ссылка), что и для проверки концепции, с использованием PHP:

$subjects=['dont','o','oo','kook','book','paper','kayak','okonoko','aaaaa','bbbb'];
$pattern='/(\w)(?:(?R)|\w?)\1/';
foreach ($subjects as $sub) {
  echo $sub." ".str_repeat('-',15-strlen($sub))."-> ";
  if (preg_match($pattern,$sub,$m)) 
      echo $m[0].(($m[0]==$sub)? "! a palindrome!\n": "\n");
  else 
      echo "sorry, no match\n";
}

Результаты

dont ------------> sorry, no match
o ---------------> sorry, no match
oo --------------> oo! a palindrome!
kook ------------> kook! a palindrome!
book ------------> oo
paper -----------> pap
kayak -----------> kayak! a palindrome!
okonoko ---------> okonoko! a palindrome!
aaaaa -----------> aaaaa! a palindrome!
bbbb ------------> bbb

Сравнение

Регулярное выражение ^((\w)(?:(?1)|\w?)\2)$ выполняет ту же работу, но вместо yes/not «содержит». 
PS: он использует определение, где «o» не является палимбромом, а дефисный формат «able-elba» - это не палиндром, а «ableelba». Называя его definition1
Когда «o» и «able-elba» являются палиндронами, называются definition2.

Сравнивая с другими "регулярными выражениями палиндрома",

  • ^((.)(?:(?1)|.?)\2)$ base-regex выше без ограничения \w, принимая «able-elba».

  • ^((.)(?1)?\2|.)$ ( @LilDevil ) Использовать definition2 (принимает «o» и «able-elba», которые отличаются также распознаванием строк «aaaaa» и «bbbb»).

  • ^((.)(?1)\2|.?)$ ( @Markus ) не обнаружил ни "kook", ни "bbbb"

  • ^((.)(?1)*\2|.?)$ ( @Csaba ) Использовать определение2.


ПРИМЕЧАНИЕ: для сравнения вы можете добавить больше слов в $subjects и строку для каждого сравниваемого регулярного выражения,

  if (preg_match('/^((.)(?:(?1)|.?)\2)$/',$sub)) echo " ...reg_base($sub)!\n";
  if (preg_match('/^((.)(?1)?\2|.)$/',$sub)) echo " ...reg2($sub)!\n";
  if (preg_match('/^((.)(?1)\2|.?)$/',$sub)) echo " ...reg3($sub)!\n";
  if (preg_match('/^((.)(?1)*\2|.?)$/',$sub)) echo " ...reg4($sub)!\n";
3
Peter Krauss

Относительно выражения PCRE (из MizardX):

/^((.)(?1)\2|.?)$/

Вы проверяли это? На моем PHP 5.3 под Win XP Pro происходит сбой: aaaba На самом деле, я немного изменил выражение выражения, чтобы прочитать:

/^((.)(?1)*\2|.?)$/

Я думаю, что происходит то, что в то время как внешняя пара символов привязана, остальные внутренние - нет. Это не совсем полный ответ, потому что, хотя он неверно передает слова «aaaba» и «aabaacaa», он действительно ошибочно указывает на «aabaaca».

Интересно, есть ли исправления для этого, а также, Правильно ли проходит тест Perl (автор JF Sebastian/Zsolt)?

Чаба Габор из Вены

2
Csaba

В Perl (см. Также ответ Жолта Ботыкай ):

$re = qr/
  .                 # single letter is a palindrome
  |
  (.)               # first letter
  (??{ $re })??     # apply recursivly (not interpolated yet)
  \1                # last letter
/x;

while(<>) {
    chomp;
    say if /^$re$/; # print palindromes
}
2
jfs

На самом деле это проще сделать с помощью строковых операций, чем с помощью регулярных выражений:

bool isPalindrome(String s1)

{

    String s2 = s1.reverse;

    return s2 == s1;
}

Я понимаю, что это на самом деле не отвечает на вопрос интервью, но вы могли бы использовать его, чтобы показать, как вы знаете лучший способ выполнения задачи, и вы не типичный "человек с молотком, который видит каждую проблему как гвоздь «.

2
Dan

Вот мой ответ на 5-й уровень Regex Golf (Человек, план). Он работает до 7 символов с помощью браузера Regexp (я использую Chrome 36.0.1985.143).

^(.)(.)(?:(.).?\3?)?\2\1$

Вот один до 9 символов

^(.)(.)(?:(.)(?:(.).?\4?)?\3?)?\2\1$

Чтобы увеличить максимальное количество символов, для которых оно будет работать, вы должны несколько раз заменить .? с (?: (.).?\n?)?.

2
pbatey

вот код PL/SQL, который сообщает, является ли данная строка палиндромом или не использует регулярные выражения:

create or replace procedure palin_test(palin in varchar2) is
 tmp varchar2(100);
 i number := 0;
 BEGIN
 tmp := palin;
 for i in 1 .. length(palin)/2 loop
  if length(tmp) > 1 then  
    if regexp_like(tmp,'^(^.).*(\1)$') = true then 
      tmp := substr(palin,i+1,length(tmp)-2);
    else 
      dbms_output.put_line('not a palindrome');
      exit;
    end if;
  end if;  
  if i >= length(palin)/2 then 
   dbms_output.put_line('Yes ! it is a palindrome');
  end if;
 end loop;  
end palin_test;
1
ankush

Как указывает ZCHudson , определить, не является ли что-то палиндромом, нельзя с помощью обычного регулярного выражения, так как множество палиндромов не является регулярным языком.

Я полностью не согласен с Airsource Ltd когда он говорит, что "это невозможно" - это не тот ответ, который ищет интервьюер. Во время моего интервью я сталкиваюсь с таким вопросом, когда сталкиваюсь с хорошим кандидатом, чтобы проверить, сможет ли он найти правильный аргумент, когда мы предложили ему сделать что-то не так. Я не хочу нанимать кого-то, кто попытается сделать что-то неправильно, если он знает лучше.

1
Nicolas

Из теории автоматов невозможно сопоставить палиандром любой длины (потому что это требует бесконечного количества памяти). Но ЭТО IS ВОЗМОЖНО, чтобы соответствовать Палиандромам Фиксированной Длины . Скажем, можно написать регулярное выражение, которое соответствует всем палиандромам длины <= 5 или <= 6 и т.д., Но не> = 5 и т.д., Где верхняя граница неясно

1
Vijeenrosh P.W

В Ruby вы можете использовать \b(?'Word'(?'letter'[a-z])\g'Word'\k'letter+0'|[a-z])\b для сопоставления слов палиндрома, таких как a, dad, radar, racecar, and redivider. PS: это регулярное выражение соответствует только палиндромные слова, которые нечетное количество букв в длину.

Давайте посмотрим, как это регулярное выражение соответствует радар. Граница слова\b совпадает в начале строки. Движок регулярных выражений входит в группу захвата «Слово». [a-z] соответствует r, который затем сохраняется в стеке для группы захвата "буква" на нулевом уровне рекурсии. Теперь движок регулярных выражений входит в первую рекурсию группы «Слово». (? 'letter' [a-z]) соответствует и захватывает a на первом уровне рекурсии. Регулярное выражение входит во вторую рекурсию группы «Слово». (? 'letter' [a-z]) захватывает d на втором уровне рекурсии. Во время следующих двух рекурсий группа захватывает a и r на уровнях три и четыре. Пятая рекурсия завершается неудачно, потому что в строке не осталось символов, которые бы соответствовали [a-z]. Двигатель регулярных выражений должен вернуться назад.

Движок регулярных выражений теперь должен попробовать второй вариант внутри группы «Слово». Второе [a-z] в регулярном выражении соответствует последнему r в строке. Двигатель теперь выходит из успешной рекурсии, возвращаясь на один уровень вверх к третьей рекурсии.

После сопоставления (& Word) двигатель достигает\k'letter + 0 '. Обратная ссылка не выполняется, потому что механизм регулярных выражений уже достиг конца строки темы. Так что это возвращается назад еще раз. Второй вариант теперь соответствует a. Движок регулярных выражений выходит из третьей рекурсии.

Движок регулярных выражений снова соответствует (& Word) и должен снова попытаться выполнить обратную ссылку. Обратная ссылка указывает +0 или текущий уровень рекурсии, который равен 2. На этом уровне группа захвата соответствует d. Обратная ссылка не выполняется, потому что следующий символ в строке - r. Снова откат, второй вариант соответствует d.

Теперь\k'letter + 0 'соответствует второму a в строке. Это связано с тем, что движок регулярных выражений вернулся при первой рекурсии, во время которой группа захвата соответствовала первой a. Движок регулярных выражений выходит из первой рекурсии.

Движок регулярных выражений теперь находится за пределами всей рекурсии. Что на этом уровне группа захвата хранится r. Теперь обратная ссылка может соответствовать финальному r в строке. Поскольку движок больше не находится внутри какой-либо рекурсии, он продолжает работу с остатком регулярного выражения после группы.\b соответствует концу строки. Конец регулярного выражения достигнут, и радар возвращается как общий матч.

1
Melih Altıntaş

У меня пока нет комментариев для комментирования, но регулярное выражение, предоставленное MizardX и измененное Csaba, может быть дополнительно изменено, чтобы оно работало в PCRE. Единственный сбой, который я обнаружил, - это строка из одного символа, но я могу проверить это отдельно.

/^((.)(?1)?\2|.)$/

Если вы можете заставить его не работать на любых других строках, пожалуйста, прокомментируйте.

1
Lil Devil

Лучшее, что вы можете сделать с регулярными выражениями, прежде чем вы исчерпаете группы захвата:

/(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?).?\9\8\7\6\5\4\3\2\1/

Это будет соответствовать всем палиндромам длиной до 19 символов.

Программное решение для всех длин тривиально:

str == str.reverse ? true : false
1
Chris
#!/usr/bin/Perl

use strict;
use warnings;

print "Enter your string: ";
chop(my $a = scalar(<STDIN>));    
my $m = (length($a)+1)/2;
if( (length($a) % 2 != 0 ) or length($a) > 1 ) { 
  my $r; 
  foreach (0 ..($m - 2)){
    $r .= "(.)";
  }
  $r .= ".?";
  foreach ( my $i = ($m-1); $i > 0; $i-- ) { 
    $r .= "\\$i";
  } 
  if ( $a =~ /(.)(.).\2\1/ ){
    print "$a is a palindrome\n";
  }
  else {
    print "$a not a palindrome\n";
 }
exit(1);
}
print "$a not a palindrome\n";
1
sapam

что вы можете сделать с Perl: http://www.perlmonks.org/?node_id=577368

1
Zsolt Botykai

Я хотел бы объяснить интервьюеру, что язык, состоящий из палиндромов, не является обычным языком, а является контекстно-свободным.

Регулярное выражение, которое будет соответствовать всем палиндромам, будет бесконечным . Вместо этого я бы предложил, чтобы он ограничил себя максимальным размером палиндромов, чтобы принять; или, если все палиндромы необходимы, используйте как минимум некоторый тип NDPA, или просто используйте простой метод обращения строк/равно.

1
Flame

мой $ pal = 'малалайам';

while($pal=~/((.)(.*)\2)/){                                 #checking palindrome Word
    $pal=$3;
}
if ($pal=~/^.?$/i){                                         #matches single letter or no letter
    print"palindrome\n";
}
else{
    print"not palindrome\n";
}
0
Kanchan Sen Laskar

Небольшое уточнение метода Airsource Ltd, в псевдокоде:

WHILE string.length > 1
    IF /(.)(.*)\1/ matches string
        string = \2
    ELSE
        REJECT
ACCEPT
0
Stewart

В JavaScript это делается путем ввода

          function palindrome(str) {
  var symbol = /\W|_/g;
  str = str.replace(symbol, "").toLowerCase();
  var palindrome = str.split("").reverse("").join("");
  return (str === palindrome);
}
0
Erik Rybalkin

Вы также можете сделать это без использования рекурсии:

\A(?:(.)(?=.*?(\1\2?)\z))*?.?\2\z

или исключить пустую строку:

\A(?=.)(?:(.)(?=.*?(\1\2?)\z))*?.?\2\z

Работает с Perl, PCRE, Ruby, Java

демо

0
Casimir et Hippolyte

\b([a-z])?([a-z])?([a-z])?\2\1\b/gi

Соответствует пятибуквенным палиндромам, таким как ссылка и каяк. Это делается с использованием (не жадного) сопоставления любых трех букв, за которыми следуют 2-я и 1-я совпадающие буквы.

Ссылка на сайт regex101 с помощью этого

0
Josh