Answers:
这个问题的答案是“不可能”。更具体地说,面试官想知道您是否在计算理论课上给予了关注。
在计算理论课上,您学习了有限状态机。有限状态机由节点和边组成。每个边都用来自有限字母的字母注释。一个或多个节点是特殊的“接受”节点,一个节点是“开始”节点。从给定单词中读取每个字母时,我们会在机器中遍历给定边沿。如果我们最终处于接受状态,那么我们就说机器“接受”了这个词。
正则表达式始终可以转换为等效的有限状态机。也就是说,接受和拒绝与正则表达式相同的单词的单词(在现实世界中,某些正则表达式语言允许使用任意函数,但这些函数不计在内)。
建立接受所有回文的有限状态机是不可能的。证明依赖于以下事实:我们可以轻松构建需要任意数量节点的字符串,即字符串
a ^ xba ^ x(例如aba,aabaa,aaabaaa,aaaabaaaa等)。
其中a ^ x是重复的x次。这至少需要x个节点,因为在看到'b'之后,我们必须倒数x次以确保它是回文。
最后,回到最初的问题,您可以告诉访问者,您可以编写一个正则表达式,以接受所有小于一定固定长度的回文。如果现实世界中有需要识别回文的应用程序,那么几乎可以肯定不会包括任意长的回文,因此,该答案将表明您可以将理论上的不可能性与现实世界中的应用区分开。尽管如此,实际的正则表达式仍会很长,比等效的4行程序要长得多(对读者来说容易练习:编写一个识别回文的程序)。
>=1.9
)在这里
尽管PCRE引擎确实支持递归正则表达式(请参阅Peter Krauss的答案),但如果没有额外的代码,则不能在ICU引擎(例如,Apple 使用的)上使用正则表达式来实现此目的。您需要执行以下操作:
这可以检测到任何回文,但确实需要循环(由于正则表达式无法计数,因此需要循环)。
$a = "teststring";
while(length $a > 1)
{
$a =~ /(.)(.*)(.)/;
die "Not a palindrome: $a" unless $1 eq $3;
$a = $2;
}
print "Palindrome";
这是不可能的。回文不是由常规语言定义的。(看,我DID学习了计算机理论)
使用Perl正则表达式:
/^((.)(?1)\2|.?)$/
但是,正如许多人指出的那样,如果您要严格的话,则不能将其视为正则表达式。正则表达式不支持递归。
abababa
。使用基于PCRE的正则表达式引擎时,不可能对每个输入都使用递归。Casimirs正则表达式使用另一种方法,即使用迭代和可变状态,非常引人入胜。
根据您的自信程度,我会给出以下答案:
我不会用正则表达式来做。这不是正则表达式的适当用法。
StackOverflow充满了诸如“正则表达式?不,他们不支持它。他们不支持它。”之类的答案。
事实是,正则表达式不再与正则语法有关。现代正则表达式具有诸如递归和平衡组之类的功能,并且其实现的可用性不断增长(例如,请参见此处的Ruby示例)。以我的观点,坚持旧的信念,即我们领域中的正则表达式只不过是编程概念而已,只会适得其反。现在不是时候让他们讨厌不再适合的单词选择了,现在是我们接受事物并继续前进的时候了。
这是Perl本身的创建者Larry Wall的引文:
(…)通常与我们所说的“正则表达式”有关,后者仅与真正的正则表达式相关。但是,该术语随着我们的模式匹配引擎的功能而增长,因此在这里我不会尝试解决语言上的必要性。但是,我通常将它们称为“ regexes”(或当我处于盎格鲁-撒克逊语境时称为“ regexen”)。
而且这里有一个博客帖子的PHP的核心开发者之一:
由于这篇文章很长,这里总结了要点:
- 在形式语言理论的背景下,程序员使用的“正则表达式”与原始的正则性概念几乎没有共通之处。
- 正则表达式(至少是PCRE)可以匹配所有上下文无关的语言。因此,它们还可以匹配格式正确的HTML和几乎所有其他编程语言。
- 正则表达式至少可以匹配某些上下文相关的语言。
- 正则表达式的匹配是NP完全的。这样,您可以使用正则表达式解决任何其他NP问题。
话虽如此,您可以使用以下命令将正则表达式与回文匹配:
^(?'letter'[a-z])+[a-z]?(?:\k'letter'(?'-letter'))+(?(letter)(?!))$
...这显然与常规语法无关。
此处提供更多信息:http : //www.regular-expressions.info/balancing.html
现在可以在Perl中完成。使用递归引用:
if($istr =~ /^((\w)(?1)\g{-1}|\w?)$/){
print $istr," is palindrome\n";
}
根据最近的部分进行了修改http://perldoc.perl.org/perlretut.html
在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
实际上,使用字符串操作比使用正则表达式更容易:
bool isPalindrome(String s1)
{
String s2 = s1.reverse;
return s2 == s1;
}
我意识到这并不能真正回答面试问题,但是您可以使用它来展示您如何更好地完成一项任务,并且您不是典型的“拿着锤子的人”,他把所有问题都看成是钉子。”
这是我对Regex Golf第五级(一个男人,一个计划)的回答。使用浏览器的Regexp,它最多可以使用7个字符(我使用的是Chrome 36.0.1985.143)。
^(.)(.)(?:(.).?\3?)?\2\1$
这是最多9个字符的字符
^(.)(.)(?:(.)(?:(.).?\4?)?\3?)?\2\1$
为了增加最大字符数,您需要反复替换。?。与(?:(。)。?\ n?)?。
如此简单且不言而喻的算法来检测包含回文的字符串:
(\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”。命名为definition1。
当“ o”和“ able-elba”为回文教鞭时,命名为definition2。
与其他“回文正则表达式”相比,
^((.)(?:(?1)|.?)\2)$
上面的base-regex \w
不受限制地接受“ able-elba”。
^((.)(?1)?\2|.)$
(@LilDevil)使用definition2(接受“ o”和“ able-elba”,因此在识别“ aaaaa”和“ bbbb”字符串方面也有所不同)。
^((.)(?1)\2|.?)$
(@Markus)未检测到“异常”,也未检测到“ bbbb”
^((.)(?1)*\2|.?)$
(@Csaba)使用definition2。
注意:要进行比较,您可以在$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";
您也可以不使用递归来做到这一点:
\A(?:(.)(?=.*?((?(2)\1\2|\1))\z))*?.?\2\z
允许一个字符:
\A(?:(?:(.)(?=.*?((?(2)\1\2|\1))\z))*?.?\2|.)\z
与Perl,PCRE一起使用
对于Java:
\A(?:(.)(?=.*?(\1\2\z|(?<!(?=\2\z).{0,1000})\1\z)))*?.?\2\z
关于PCRE表达(来自MizardX):
/^((.)(?1)\2|.?)$/
你测试过了吗?在Win XP Pro下,在我的PHP 5.3上它失败了:aaaba实际上,我对表达式expression进行了一些修改,使其显示为:
/^((.)(?1)*\2|.?)$/
我认为正在发生的事情是,当外在的两个角色锚定时,其余的内在的角色却没有。这不是一个完整的答案,因为尽管它错误地传递了“ aaaba”和“ aabaacaa”,但在“ aabaaca”上却确实失败了。
我想知道是否对此进行了修复,并且Perl示例(由JF Sebastian / Zsolt设计)是否可以正确通过我的测试?
维也纳的Csaba Gabor
在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
}
#!/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";
根据自动机理论,不可能匹配任何长度的花粉症(因为这需要无限量的记忆力)。但是可以匹配固定长度的回历。说有可能编写一个正则表达式来匹配长度<= 5或<= 6等的所有回文,但在上限不清楚的情况下不匹配> = 5等
在Ruby中,您可以使用\b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z])\b
匹配回文词,例如a, dad, radar, racecar, and redivider
。ps:此正则表达式仅匹配长度为奇数个字母的回文词。
让我们看看这个正则表达式如何匹配雷达。单词边界\ b在字符串的开头匹配。正则表达式引擎进入捕获组“单词”。[az]匹配r,然后将其以递归级别零存储在捕获组“ letter”的堆栈中。现在,正则表达式引擎进入“单词”组的第一个递归。(?'letter'[az])匹配并捕获递归级别1的a。正则表达式进入组“单词”的第二次递归。(?'letter'[az])在第二递归级别捕获d。在接下来的两次递归中,该小组在第三和第四级捕获a和r。第五次递归失败,因为在字符串中没有[az]要匹配的字符。正则表达式引擎必须回溯。
正则表达式引擎现在必须在“单词”组内尝试第二种替代方法。正则表达式中的第二个[az]与字符串中的最后一个r相匹配。引擎现在从成功的递归退出,将一级返回到第三级递归。
匹配(&word)后,引擎达到\ k'letter + 0'。后向引用失败,因为正则表达式引擎已到达主题字符串的末尾。因此,它再次回溯。现在第二个替代项与a匹配。正则表达式引擎从第三次递归退出。
正则表达式引擎再次匹配(&word),并且需要再次尝试反向引用。向后引用指定+0或当前递归级别,即2。在此级别,捕获组与d匹配。后向引用失败,因为字符串中的下一个字符是r。再次回溯,第二个替代项匹配d。
现在,\ k'letter + 0'匹配字符串中的第二个a。这是因为正则表达式引擎返回了第一个递归,在此期间捕获组与第一个a匹配。正则表达式引擎退出第一个递归。
正则表达式引擎现在返回所有递归之外。表示在这个级别上,捕获组存储了r。现在,后向引用可以匹配字符串中的最后一个r。由于引擎不再处于任何递归内,因此在该组之后继续进行正则表达式的其余部分。\ b在字符串末尾匹配。到达正则表达式的末尾,并返回雷达作为整体匹配项。
这是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;
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";
}
该正则表达式将检测多达22个字符的回文,而忽略空格,制表符,逗号和引号。
\b(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*(?:(\w)[ \t,'"]*\11?[ \t,'"]*\10|\10?)[ \t,'"]*\9|\9?)[ \t,'"]*\8|\8?)[ \t,'"]*\7|\7?)[ \t,'"]*\6|\6?)[ \t,'"]*\5|\5?)[ \t,'"]*\4|\4?)[ \t,'"]*\3|\3?)[ \t,'"]*\2|\2?))?[ \t,'"]*\1\b
在这里玩:https : //regexr.com/4tmui