それは私が答えることができなかったインタビューの質問でした:
正規表現を使用して文字列が回文であることを確認するにはどうすればよいですか?
psすでに「指定された文字列が回文かどうかを確認する方法は?」という質問があり、さまざまな言語で多くの回答が得られますが、正規表現を使用する回答はありません。
それは私が答えることができなかったインタビューの質問でした:
正規表現を使用して文字列が回文であることを確認するにはどうすればよいですか?
psすでに「指定された文字列が回文かどうかを確認する方法は?」という質問があり、さまざまな言語で多くの回答が得られますが、正規表現を使用する回答はありません。
回答:
この質問への答えは「不可能」です。より具体的には、インタビュアーはあなたが計算理論のクラスで注意を払ったかどうか疑問に思っています。
計算理論のクラスでは、有限状態機械について学びました。有限状態機械はノードとエッジで構成されています。各エッジには、有限のアルファベットの文字で注釈が付けられています。1つ以上のノードが特別な「受け入れ」ノードであり、1つのノードが「開始」ノードです。各文字は特定の単語から読み取られるため、マシンの特定のエッジをトラバースします。受け入れ状態になると、マシンはその単語を「受け入れる」と言います。
正規表現は、常に同等の有限状態マシンに変換できます。つまり、正規表現と同じ単語を受け入れたり拒否したりします(現実の世界では、一部の正規表現言語では任意の関数を使用できますが、これらはカウントされません)。
すべてのパリンドロームを受け入れる有限状態機械を構築することは不可能です。証明は、任意の数のノードを必要とする文字列、つまり文字列を簡単に作成できるという事実に依存しています
a ^ xba ^ x(例:aba、aabaa、aaabaaa、aaaabaaaa、...)
ここで、a ^ xはx回繰り返されます。「b」が表示された後、回文であることを確認するためにx回カウントする必要があるため、これには少なくともxノードが必要です。
最後に、元の質問に戻り、有限の固定長より小さいすべてのパリンドロームを受け入れる正規表現を記述できることをインタビュアーに伝えることができます。パリンドロームの特定を必要とする現実のアプリケーションがある場合、それはほぼ確実に任意の長さのものを含まないため、この回答は、理論的な不可能性を現実のアプリケーションと区別できることを示しています。それでも、実際の正規表現はかなり長く、同等の4行プログラムよりもはるかに長くなります(読者にとって簡単な練習:回文を識別するプログラムを作成してください)。
>=1.9
)の良い記事がここにあります
一方でPCREのエンジンがサポート再帰的な正規表現を(参照しピーター・クラウスによって答えを)、あなたは上の正規表現を使用することはできませんICUの余分なコードなしでこれを達成するために(例えば、Appleが使用されるような)エンジン。次のようなことをする必要があります:
これはパリンドロームを検出しますが、ループが必要です(正規表現がカウントできないため、ループが必要になります)。
$a = "teststring";
while(length $a > 1)
{
$a =~ /(.)(.*)(.)/;
die "Not a palindrome: $a" unless $1 eq $3;
$a = $2;
}
print "Palindrome";
不可能です。回文は通常の言語では定義されていません。(参照、私は計算理論で何かを学びました)
Perl正規表現の場合:
/^((.)(?1)\2|.?)$/
多くの人が指摘したように、厳密にしたい場合、これは正規表現とは見なされません。正規表現は再帰をサポートしていません。
abababa
。PCREベースの正規表現エンジンを使用する場合は、すべての入力に対して再帰を使用して機能させることはできません。Casimirs regexは、反復と可変状態を使用する別のアプローチを使用しており、非常に魅力的です。
StackOverflowには、「正規表現?いいえ、サポートされていません。サポートできません。」などの回答が満載です。
真実は、正規表現がもはや正規の文法とは何の関係もないということです。最新の正規表現は、再帰やバランスグループなどの機能を備えており、それらの実装の可用性はますます高まっています(たとえば、Rubyの例を参照してください)。私の意見では、私たちの分野の正規表現はプログラミングの概念以外のものであるという古い信念にぶら下がっています。もはや最も適切ではない単語の選択を彼らに嫌うのではなく、物事を受け入れて次に進む時がきました。
これは、Perl自体の作成者であるLarry Wallからの引用です。
(…)一般に、「正規表現」と呼ばれるものと関係があります。これは、実際の正規表現にわずかに関連しているだけです。それにもかかわらず、この用語はパターンマッチングエンジンの機能によって拡大したため、ここでは言語の必要性に対抗するつもりはありません。しかし、私は一般的にそれらを「正規表現」(または私がアングロサクソンムードにいるときは「正規表現」)と呼びます。
そして、PHPのコア開発者の1人によるブログ投稿は次のとおりです。
記事がかなり長かったので、ここで主なポイントの要約を示します。
- プログラマーが使用する「正規表現」は、正式な言語理論の文脈における規則性の元の概念との共通点がほとんどありません。
- 正規表現(少なくとも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の5番目のレベル(男性、計画)に対する私の答えです。ブラウザーの正規表現で最大7文字まで機能します(Chrome 36.0.1985.143を使用しています)。
^(.)(.)(?:(.).?\3?)?\2\1$
これは最大9文字の1つです
^(.)(.)(?:(.)(?:(.).?\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」はそうです。定義に名前を付ける1。
「o」と「able-elba」が回文式の場合は、definition2という名前を付けます。
別の「パリンドローム正規表現」と比較すると、
^((.)(?:(?1)|.?)\2)$
上記の基本正規表現は\w
制限なしで、「able-elba」を受け入れます。
^((.)(?1)?\2|.)$
(@LilDevil)definition2を使用します(「o」と「able-elba」を受け入れるため、「aaaaa」と「bbbb」の文字列の認識も異なります)。
^((.)(?1)\2|.?)$
(@Markus)「kook」も「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実際、私は次のように式の式を少し変更しました:
/^((.)(?1)*\2|.?)$/
何が起こっているのかと思いますが、外側の文字のペアは固定されていますが、残りの内側の文字は固定されていません。「aaaba」と「aabaacaa」は誤って渡されますが、「aabaaca」では正しく失敗しないため、これは完全な答えではありません。
これの修正はありますか、また、Perlの例(JF Sebastian / Zsoltによる)は私のテストに正しく合格しますか?
ウィーン発クサバガボール
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
}
ZCHudsonによって指摘されたように、パリンドロームのセットは通常の言語ではないため、何かがパリンドロームであるかどうかを判断し、通常の正規表現では実行できません。
Airsource Ltd が「不可能だ」とはインタビュアーが求めている種類の答えではないと彼が言ったとき、私は完全に反対します。面接の際、良い候補に直面したときにこの種の質問をし、私たちが何か間違ったことを提案したときに正しい議論を見つけることができるかどうかを確認します。私は彼がより良いものを知っているなら間違った方法で何かをしようとする誰かを雇いたくありません。
perlでできること:http : //www.perlmonks.org/? node_id =577368
キャプチャグループが不足する前に、正規表現を使用して実行できる最善の方法:
/(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?).?\9\8\7\6\5\4\3\2\1/
これは、長さが19文字までのすべてのパリンドロームと一致します。
すべての長さをプログラムで解決するのは簡単です。
str == str.reverse ? true : false
#!/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";
オートマトン理論から、あらゆる長さのパリアンドロームと一致させることは不可能です(無限のメモリを必要とするため)。ただし、固定長のPaliandromesと一致させることは可能です。長さが<= 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と一致し、再帰レベル0でキャプチャグループ "letter"のスタックに格納されます。これで、正規表現エンジンがグループ「単語」の最初の再帰に入ります。(? 'letter' [az])は、再帰レベル1のaに一致してキャプチャします。正規表現は、グループ「単語」の2番目の再帰に入ります。(? 'letter' [az])は、再帰レベル2でdをキャプチャします。次の2回の再帰の間に、グループはレベル3と4でaとrをキャプチャします。[az]に一致する文字が文字列に残っていないため、5回目の再帰は失敗します。正規表現エンジンはバックトラックする必要があります。
正規表現エンジンは、グループ「単語」内で2番目の代替案を試す必要があります。正規表現の2番目の[az]は、文字列の最後のrと一致します。これで、エンジンは正常な再帰を終了し、1つのレベルから3番目の再帰に戻ります。
(&word)に一致した後、エンジンは\ k'letter + 0 'に到達します。正規表現エンジンがすでに件名の文字列の終わりに達しているため、後方参照は失敗します。そのため、もう一度バックトラックします。2番目の選択肢はaに一致します。正規表現エンジンは3番目の再帰を終了します。
正規表現エンジンは再び一致し(&word)、後方参照を再度試行する必要があります。後方参照は+0または現在の再帰レベル(2)を指定します。このレベルでは、キャプチャグループはdに一致しました。文字列の次の文字がrであるため、後方参照は失敗します。再びバックトラックすると、2番目の代替案はdに一致します。
これで、\ k'letter + 0 'は文字列の2番目の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