如何使用正则表达式检查一个字符串是否为回文?

109

这是我无法回答的面试问题:

如何使用正则表达式检查字符串是否为回文?

附:已经有一个问题 "如何检查给定的字符串是否为回文?",它提供了许多不同语言的答案,但没有使用正则表达式的答案。


1
https://dev59.com/l3A65IYBdhLWcg3wxBmr 可以提供一个思路。 - unknown_boundaries
3
现在(2018年)如果您正在寻找“回文正则表达式”,请参阅Prakhar的链接中关于PCRE支持递归模式的讨论,以及我的递归正则表达式和比较 - Peter Krauss
32个回答

7
现在可以使用Perl完成此操作。使用递归引用:
if($istr =~ /^((\w)(?1)\g{-1}|\w?)$/){
    print $istr," is palindrome\n";
}

基于最近的一部分进行修改 http://perldoc.perl.org/perlretut.html


6
在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 

1
命名捕获组并不严格属于正则表达式。http://www.willamette.edu/~fruehr/LLC/lab5.html - Steve Moser
2
你是正确的。这正是我指出你必须使用命名捕获组的原因。 - Taylor
1
有没有人能向新手一样逐个解释一下这个 RE 的字符呢?我理解以下所有内容(逗号分隔“原子”)/,\A,(,|,\w,|,(,(,\w,),),),\z,/,x,但是我不理解这些:?<p>、?:、?<l>、\g<p>、\k<l+0>,我正在使用 rubular.com 寻求帮助,它似乎理解这个正则表达式(自然地),但这并不能帮助我看到它,即使是“对于完整的 Ruby 正则表达式指南,请参见 Pickaxe。”也没有帮助,因为与“Pickaxe”链接的网站没有解释我无法理解的原子。我知道 ? 在 a 之后匹配零个或一个 a,但 ? 在字符之前又代表什么呢? - Kevin Ford The Submariner
1
啊,命名捕获组!太棒了。@SteveMoser那个链接已经失效了,但我找到了另一个。感谢Taylor提到它们,否则我根本不知道?<p>和?<l>以及?:(非捕获捕获组)和\g<p>和\k<l+0>是什么意思。不过,我还是看不出?<p>|是什么意思。|不是表示“或者”吗?我找不到关于正则表达式中使用管道符的相关文档。我仍然很想看到这个非常好的正则表达式的详细解释。 - Kevin Ford The Submariner
@KevinFordTheSubmariner 我认为 (?<p>|.. 语法只是表示允许匹配空值或其他备选项的剩余部分。而 (?<p>..) 语法则是一个命名捕获组,可以使用 \k<p> 引用精确匹配,而不是引用组号。+0 表示在相同递归级别上。 - Scratte

6

递归正则表达式可以实现!

检测包含回文字符串的简单而自明的算法:

   (\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)$的作用与“包含”不同,而是作为是/否的判断。
PS:它使用的定义是“o”不是回文,“able-elba”连字符格式不是回文,但“ableelba”是回文。将其命名为定义1
当“o”和“able-elba”是回文时,命名为定义2

与另一个“回文正则表达式”进行比较,

  • ^((.)(?:(?1)|.?)\2)$是基本正则表达式,没有\w限制,接受“able-elba”。

  • ^((.)(?1)?\2|.)$ (@LilDevil) 使用定义2(接受“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";

我尝试了一下,它似乎匹配了所有的回文:^((.)(?:(?1)|.?)\2|(.)\3*)$ - Hao Wu

5

这是我对Regex Golf的第五关(一个男人,一个计划)的答案。它适用于浏览器的正则表达式,最多可匹配7个字符(我使用的是Chrome 36.0.1985.143)。

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

这里有一个针对最多9个字符的例子。
^(.)(.)(?:(.)(?:(.).?\4?)?\3?)?\2\1$

为了增加它所适用的最大字符数,您需要反复使用.?替换为(?:(.).?\n?)?

1
我用稍微少一点字符的方式实现了这个, ^(.)(.)(.)?.?\3\2\1$ - Ben Ellis
非常感谢你把它告诉我 :-) - U13-Forward
为什么其他人有13,但这个是19? - U13-Forward

4
/\A(?<a>|.|(?:(?<b>.)\g<a>\k<b+0>))\z/

这适用于Oniguruma引擎(用于Ruby)

摘自Pragmatic Bookshelf


4

实际上,使用字符串操作比正则表达式更容易:

bool isPalindrome(String s1)

{

    String s2 = s1.reverse;

    return s2 == s1;
}

我知道这并不能完全回答面试问题,但你可以利用这点来展示你知道一种更好的完成任务的方式,而不是像典型的“拿着锤子,把所有问题都看成钉子”的人。

虽然我很喜欢这个答案,但我认为如果使用BreakIterator来正确地将字符串分割成可视字符,你会得到额外的分数。 - Hakanai

4
关于PCRE表达式(来自MizardX):
/ ^((.)(?1)\ 2 |。?)$ /
你测试过它吗?在我的PHP 5.3下面,它在Win XP Pro上无法通过aaaba测试。实际上,我稍微修改了一下表达式,变成了:
/ ^((.)(?1)* \ 2 |。?)$ /
我认为正在发生的是,尽管外部字符对齐,但其余内部字符却没有。这并不是完整的答案,因为虽然它会错误地通过“aaaba”和“aabaacaa”,但在“aabaaca”上确实会失败。
我想知道是否有解决方法以及JF Sebastian / Zsolt的Perl示例是否正确通过了我的测试?
来自维也纳的Csaba Gabor

3
在Perl中(另请参见Zsolt Botykai的答案):
$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

以下是使用正则表达式判断给定字符串是否为回文字符串的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;

2
my $pal='malayalam';

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";
}

3
虽然这段代码可能回答了问题,但提供如何或为什么解决问题的补充说明会增加答案的长期价值。 - Donald Duck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接