จะตรวจสอบได้อย่างไรว่าสตริงเป็น palindrome โดยใช้นิพจน์ทั่วไป


94

นั่นเป็นคำถามสัมภาษณ์ที่ฉันไม่สามารถตอบได้:

จะตรวจสอบได้อย่างไรว่าสตริงเป็น palindrome โดยใช้นิพจน์ทั่วไป

ps มีคำถามอยู่แล้ว " จะตรวจสอบได้อย่างไรว่าสตริงที่ระบุเป็น palindrome หรือไม่ " และให้คำตอบมากมายในภาษาต่างๆ แต่ไม่มีคำตอบที่ใช้นิพจน์ทั่วไป


1
stackoverflow.com/questions/3644266/…ให้ไอเดียได้ครับ.
Prakhar

2
สำหรับในปัจจุบัน (2018) และผู้ที่กำลังมองหาที่ "regex palindrome" ดูการอภิปรายเกี่ยวกับ PCRE สนับสนุนรูปแบบ recursiveที่ลิงค์ Prakhar และฉันregex recursiveด้านล่างที่มีการเปรียบเทียบ
Peter Krauss

คำตอบ:


155

คำตอบสำหรับคำถามนี้คือ "เป็นไปไม่ได้" โดยเฉพาะอย่างยิ่งผู้สัมภาษณ์สงสัยว่าคุณให้ความสนใจในชั้นเรียนทฤษฎีการคำนวณของคุณหรือไม่

ในชั้นเรียนทฤษฎีการคำนวณของคุณคุณได้เรียนรู้เกี่ยวกับเครื่องจักรที่มีสถานะ จำกัด เครื่องที่มีสถานะ จำกัด ประกอบด้วยโหนดและขอบ ขอบแต่ละด้านมีการใส่คำอธิบายประกอบด้วยตัวอักษรแบบ จำกัด โหนดอย่างน้อยหนึ่งโหนดเป็นโหนด "ยอมรับ" แบบพิเศษและหนึ่งโหนดคือโหนด "เริ่มต้น" เนื่องจากแต่ละตัวอักษรถูกอ่านจากคำที่กำหนดเราจึงข้ามขอบที่กำหนดในเครื่อง ถ้าเราอยู่ในสถานะยอมรับเราก็บอกว่าเครื่อง "ยอมรับ" คำนั้น

นิพจน์ทั่วไปสามารถแปลเป็นเครื่องสถานะ จำกัด ที่เทียบเท่าได้เสมอ นั่นคือคำที่ยอมรับและปฏิเสธคำเดียวกันกับนิพจน์ทั่วไป (ในโลกแห่งความเป็นจริงภาษา regexp บางภาษาอนุญาตให้ใช้ฟังก์ชันตามอำเภอใจซึ่งจะไม่นับรวม)

เป็นไปไม่ได้ที่จะสร้างเครื่องจักรที่มีสถานะ จำกัด ที่ยอมรับ palindromes ทั้งหมด การพิสูจน์ขึ้นอยู่กับข้อเท็จจริงที่ว่าเราสามารถสร้างสตริงที่ต้องการโหนดจำนวนมากโดยพลการนั่นคือสตริง

a ^ xba ^ x (เช่น aba, aabaa, aaabaaa, aaaabaaaa, .... )

โดยที่ ^ x คือ x คูณซ้ำ สิ่งนี้ต้องใช้อย่างน้อย x โหนดเพราะหลังจากเห็น 'b' เราต้องนับย้อน x คูณเพื่อให้แน่ใจว่าเป็นพาลินโดรม

ในที่สุดเมื่อกลับไปที่คำถามเดิมคุณสามารถบอกผู้สัมภาษณ์ได้ว่าคุณสามารถเขียนนิพจน์ทั่วไปที่ยอมรับ palindromes ทั้งหมดที่มีขนาดเล็กกว่าความยาวคงที่ จำกัด หากเคยมีแอปพลิเคชันในโลกแห่งความเป็นจริงที่ต้องระบุ palindromes ก็แทบจะไม่รวมถึงสิ่งที่ยาวตามอำเภอใจดังนั้นคำตอบนี้จะแสดงให้เห็นว่าคุณสามารถแยกความแตกต่างที่เป็นไปไม่ได้ทางทฤษฎีจากแอปพลิเคชันในโลกแห่งความเป็นจริง อย่างไรก็ตาม regexp ที่แท้จริงจะค่อนข้างยาวและยาวกว่าโปรแกรม 4 บรรทัดที่เทียบเท่ากันมาก (แบบฝึกหัดง่าย ๆ สำหรับผู้อ่าน: เขียนโปรแกรมที่ระบุ palindromes)


7
@SteveMoser ใน Ruby 1.9.x นิพจน์ทั่วไปไม่ใช่ Regular อีกต่อไป (ในความหมายของ Automata Theory) ดังนั้นสิ่งต่างๆเช่นการตรวจสอบ palindromes จึงเป็นไปได้ อย่างไรก็ตามสำหรับเจตนาและจุดประสงค์ไม่สามารถตรวจสอบ palindromes ด้วยนิพจน์ทั่วไปได้ (เหมาะสมหรือไม่)

1
@SteveMoser มีการเขียนโปรแกรมนิพจน์ทั่วไปของ Ruby ที่ดี ( >=1.9) ที่นี่

@ จอห์นถูกต้องดังนั้นในบริบทของคำถาม Jose ถูกและ hqt ผิด
Steve Moser

2
ในทางวิชาการนิพจน์ทั่วไปมีขอบเขตเฉพาะ (กำหนด DFA) ในความเป็นจริงเอนจิ้น regexp จำนวนมาก (Perl และเป็นญาติเป็นหลัก) สนับสนุนการอ้างอิงย้อนกลับที่ละเมิดคำจำกัดความทางวิชาการ (กลายเป็น NFA หรือแม้แต่ในวงกว้าง) ดังนั้นคำถามนี้จึงมีคำตอบที่แตกต่างกันขึ้นอยู่กับกรอบอ้างอิงของผู้ถาม
jiggy

ในการทดสอบปากเปล่า zou shoulsd ให้ใช้ "formalz เป็นไปไม่ได้" แต่คุณควรชี้ให้เห็นว่าเครื่องมือ regex บางตัวอนุญาต
Oliver A.

46

ในขณะที่เอ็นจิ้น PCREรองรับนิพจน์ทั่วไปแบบวนซ้ำ (ดูคำตอบของ Peter Krauss ) คุณไม่สามารถใช้ regex บนเอ็นจิ้น ICU (ตามที่ Apple ใช้เป็นต้น) เพื่อให้บรรลุสิ่งนี้โดยไม่ต้องใช้รหัสเพิ่มเติม คุณจะต้องทำสิ่งนี้:

สิ่งนี้ตรวจพบ palindrome ใด ๆ แต่ต้องมีการวนซ้ำ (ซึ่งจะต้องใช้เนื่องจากนิพจน์ทั่วไปไม่สามารถนับได้)

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

4
คำตอบที่ดี. คำถามไม่ได้ถามถึง regexp เดียวที่ตรวจจับ palindrome ได้ทันที แต่ถามถึงวิธีการตรวจจับ palindromes ที่ใช้ regexps ขอแสดงความยินดีกับความเข้าใจของคุณในการพิจารณาด้วยวิธีนี้
Stewart

1
ดูการจับคู่ที่ง่ายที่สุด (โดยไม่มีการปรับแต่งสตริง) โดยใช้ regex เพียงรายการเดียวstackoverflow.com/a/48608623/287948
Peter Krauss

ขอบคุณ @PeterKrauss ไม่ทราบว่า PCRE มีการเรียกซ้ำ อ้างอิงคำตอบของคุณ
Airsource Ltd

33

มันเป็นไปไม่ได้. Palindromes ไม่ได้กำหนดโดยภาษาปกติ (ดูฉันเรียนรู้บางอย่างในทฤษฎีการคำนวณ)


2
เอ็นจิ้นนิพจน์ทั่วไปส่วนใหญ่จับได้มากกว่าภาษาทั่วไป (net สามารถจับวงเล็บที่ตรงกันได้เป็นต้น) regexes มาตรฐานเท่านั้นที่ จำกัด ไว้ที่ lang ปกติ
Santiago Palladino

คำถามใช้คำว่า "นิพจน์ทั่วไป" แม้ว่า ... ดังนั้นคำตอบของ ZCHudson จึงถูกต้อง
paxos1977

2
@austirg: คำตอบของ ZCHudson ถูกต้อง แต่ไม่สมบูรณ์ นิพจน์ทั่วไปที่ใช้ในภาษาโปรแกรมสมัยใหม่และนิพจน์ทั่วไปที่ใช้ในคลาส CS เชิงทฤษฎีเป็นสัตว์ร้ายที่แตกต่างกัน คำว่าเป็นเพียงมรดกทางประวัติศาสตร์ ดูstackoverflow.com/questions/233243#235199และคำตอบของฉัน
jfs

2
@JF Sebastian - ฉันต้องเห็นด้วยกับ Austirg ในเรื่องนี้ เมื่อมีการใช้คำว่านิพจน์ทั่วไปโดยไม่มีภาษาโปรแกรมเฉพาะที่กล่าวถึงนอกเหนือจากข้อกำหนดของ comp sci จะใช้ ไม่ใช่ทุกภาษาที่รองรับ regexes ที่สามารถทำได้ดังนั้นเราไม่ควรถือว่าภาษาที่ใช้ในที่นี้ทำ
Rontologist

@Rontologist: ฉันไม่เห็นข้อ จำกัด ในการเลือกภาษาโปรแกรมในคำถามดังนั้นจึงอนุญาตให้ใช้ภาษาใดก็ได้ ดูด้านขวา: ความหมายของ "นิพจน์ทั่วไป" ในคำถามที่เกี่ยวข้องคืออะไร? มีการกล่าวถึงภาษาโปรแกรมเฉพาะหรือไม่
jfs

28

ด้วย Perl regex:

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

แม้ว่าจะมีหลายคนชี้ให้เห็นว่านี่ไม่สามารถถือเป็นนิพจน์ทั่วไปได้หากคุณต้องการเข้มงวด นิพจน์ทั่วไปไม่รองรับการเรียกซ้ำ


สิ่งนี้ใช้ไม่ได้ใน PCRE (ไม่ตรงกับ "ababa") แต่ใช้งานได้ใน Perl 5.10
newacct

คุณพูดถูก PCRE ดูเหมือนจะถือว่าการเรียกซ้ำเป็นกลุ่มอะตอมในขณะที่ Perl อนุญาตให้มีการย้อนกลับภายใน ฉันไม่คิดว่าจะตรวจสอบใน PCRE ได้
Markus Jarderot

1
น่าแปลกที่ใช้ไม่ได้กับภาษาที่ไม่ใช่ภาษาละตินเช่นภาษาอาร์เมเนีย
Temujin

4
@Temujin อาจเป็นเพราะอักขระ Unicode ถูกจับคู่เป็นไบต์ที่เข้ารหัส (เพิ่ม/uตัวปรับแต่ง ) หรือเนื่องจากอักขระ combinator (แทนที่.ด้วย\Xลำดับการหลีกเลี่ยง )
Markus Jarderot

1
รูปแบบของฉันไม่ทำงานใน PCRE มันทำงานใน Perl รูปแบบของคุณล้มเหลวเมื่อสตริงย่อยซ้ำ ตัวอย่างเช่นabababa. เป็นไปไม่ได้ที่จะทำให้มันทำงานร่วมกับการเรียกซ้ำสำหรับทุกอินพุตเมื่อใช้เอนจิ้น regex บน PCRE Casimirs regex ใช้วิธีการที่แตกต่างกันโดยใช้การวนซ้ำและสถานะที่เปลี่ยนแปลงได้และค่อนข้างน่าสนใจ
Markus Jarderot

15

ต่อไปนี้เป็นวิธีตรวจจับ palindromes 4 ตัวอักษร (เช่น: โฉนด) สำหรับอักขระประเภทใดก็ได้:

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

นี่คือวิธีตรวจจับ palindromes 5 ตัวอักษร (เช่นเรดาร์) โดยตรวจหาตัวอักษรเท่านั้น:

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

ดูเหมือนว่าเราต้องการ regex ที่แตกต่างกันสำหรับความยาวของคำแต่ละคำ โพสต์นี้ในรายชื่อส่งเมล Python มีรายละเอียดบางอย่างเกี่ยวกับสาเหตุ (Finite State Automata และการปั๊มคำขยาย)


14

ขึ้นอยู่กับว่าคุณมั่นใจแค่ไหนฉันจะให้คำตอบนี้:

ฉันจะไม่ทำด้วยสีหน้าปกติ ไม่ใช่การใช้นิพจน์ทั่วไปที่เหมาะสม


3
ฉันหวังว่าคุณจะให้คำอธิบายเพิ่มเติมเล็กน้อยเพื่อแสดงให้เห็นว่าคุณเข้าใจข้อ จำกัด ของ regex จริงๆ คำตอบง่ายๆของคุณอาจแปลว่า "ฉันนิ่งงัน"
Scott Wegner

ดังนั้นประโยคอ้างอิงที่เขาให้
Will Bickford

13

ใช่คุณสามารถทำได้ใน. Net!

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

ตรวจสอบได้ที่นี่ ! เป็นโพสต์ที่วิเศษมาก!


1
จุดรวมของ. NET ปรุงแต่ง Regex ก็คือพวกมันไม่ปกติเพราะไม่ใช่ออโตมาตะที่ จำกัด พวกเขาไม่ได้เป็นนิพจน์ทั่วไปในแง่ทฤษฎี
แมว

12

StackOverflow เต็มไปด้วยคำตอบเช่น "นิพจน์ทั่วไปหรือไม่ไม่รองรับไม่รองรับ"

ความจริงก็คือนิพจน์ทั่วไปไม่มีอะไรเกี่ยวข้องกับไวยากรณ์ปกติอีกต่อไป ฟังก์ชันคุณลักษณะของนิพจน์ทั่วไปที่ทันสมัยเช่นการเรียกซ้ำและการปรับสมดุลกลุ่มและความพร้อมใช้งานของการนำไปใช้นั้นเพิ่มขึ้นเรื่อย ๆ (ดูตัวอย่าง Ruby ที่นี่เป็นต้น) ในความคิดของฉันการยึดติดกับความเชื่อเดิม ๆ ว่านิพจน์ทั่วไปในสาขาของเราเป็นอะไรก็ได้ แต่แนวคิดการเขียนโปรแกรมเป็นเพียงการต่อต้าน แทนที่จะเกลียดพวกเขาสำหรับคำว่าทางเลือกที่ไม่เหมาะสมที่สุดอีกต่อไปถึงเวลาแล้วที่เราต้องยอมรับสิ่งต่างๆและก้าวต่อไป

นี่คือคำพูดจาก Larry Wallผู้สร้าง Perl เอง:

(…) โดยทั่วไปจะเกี่ยวข้องกับสิ่งที่เราเรียกว่า "นิพจน์ทั่วไป" ซึ่งเกี่ยวข้องกับนิพจน์ทั่วไปจริงเพียงเล็กน้อยเท่านั้น อย่างไรก็ตามคำนี้เติบโตขึ้นพร้อมกับความสามารถของเครื่องมือจับคู่รูปแบบของเราดังนั้นฉันจะไม่พยายามต่อสู้กับความจำเป็นทางภาษาที่นี่ อย่างไรก็ตามโดยทั่วไปฉันจะเรียกพวกเขาว่า "regexes" (หรือ "regexen" เมื่อฉันอยู่ในอารมณ์แองโกล - แซกซอน)

และนี่คือบล็อกโพสต์โดยหนึ่งในนักพัฒนาหลักของ PHP :

เนื่องจากบทความค่อนข้างยาวจึงสรุปประเด็นหลักดังนี้

  • "นิพจน์ทั่วไป" ที่โปรแกรมเมอร์ใช้มีความเหมือนกันน้อยมากกับแนวคิดดั้งเดิมของความสม่ำเสมอในบริบทของทฤษฎีภาษาที่เป็นทางการ
  • นิพจน์ทั่วไป (อย่างน้อย PCRE) สามารถจับคู่ภาษาที่ไม่มีบริบทได้ทั้งหมด ด้วยเหตุนี้จึงสามารถจับคู่ HTML ที่มีรูปแบบดีและภาษาโปรแกรมอื่น ๆ ได้ทั้งหมด
  • นิพจน์ทั่วไปสามารถจับคู่ภาษาที่คำนึงถึงบริบทได้เป็นอย่างน้อย
  • การจับคู่นิพจน์ทั่วไปเป็นแบบ NP-complete ดังนั้นคุณสามารถแก้ปัญหา NP อื่น ๆ โดยใช้นิพจน์ทั่วไป

ดังที่กล่าวไว้คุณสามารถจับคู่ palindromes กับ regexes ได้โดยใช้สิ่งนี้:

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

... ซึ่งเห็นได้ชัดว่าไม่มีอะไรเกี่ยวข้องกับไวยากรณ์ปกติ
ข้อมูลเพิ่มเติมที่นี่: http://www.regular-expressions.info/balancing.html


9

ดังที่ได้กล่าวไปแล้วไม่มี regexp เดียวที่จะตรวจจับ palindrome ทั่วไปออกจากกล่อง แต่ถ้าคุณต้องการตรวจจับ palindromes ที่มีความยาวสูงสุดคุณสามารถใช้สิ่งต่างๆเช่น

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


6

ในทับทิมคุณสามารถใช้กลุ่มการจับภาพที่มีชื่อ ดังนั้นสิ่งนี้จะได้ผล -

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
กลุ่มการดักจับที่มีชื่อไม่ใช่นิพจน์ทั่วไป willamette.edu/~fruehr/LLC/lab5.html
Steve Moser

2
คุณถูก. นั่นเป็นเหตุผลที่ผมชี้ให้เห็นโดยเฉพาะว่าคุณจะต้องใช้กลุ่มการบันทึกที่มีชื่อ
Taylor

มีใครสามารถอธิบายได้โดยบังเอิญหรือไม่ว่า RE character by character สำหรับมือใหม่? ฉันเข้าใจสิ่งต่อไปนี้ทั้งหมด (คอมมาแยก 'อะตอม') /, \ A, (, |, \ w, |, (, (, \ w,),),), \ z, /, x แต่ฉันไม่ ไม่เข้าใจสิ่งเหล่านี้หรือไม่ <p>,?:,? <l>, \ g <p>, \ k <l + 0> และฉันกำลังใช้ rubular.com เพื่อขอความช่วยเหลือและดูเหมือนว่าจะเข้าใจ RE ( ตามธรรมชาติ) แต่นั่นไม่ได้ช่วยให้ฉันเห็นและแม้แต่ "สำหรับคู่มือ Ruby regex ฉบับสมบูรณ์โปรดดูที่ Pickaxe" ไม่ได้ช่วยอะไรสำหรับไซต์ที่เชื่อมโยงกับ 'Pickaxe' ไม่ได้อธิบายถึงอะตอมที่ฉันไม่เข้าใจ ฉันรู้ว่า ? ปฏิบัติตามการจับคู่ Zero หรือหนึ่งใน a แต่? นำหน้าตัวละคร?
Kevin Ford เรือดำน้ำ

อ่าตั้งชื่อกลุ่มจับ ! ดี. @SteveMoser ว่าตอนนี้ลิงค์เสีย แต่ผมพบอีก ขอบคุณเทย์เลอร์ที่พูดถึงพวกเขาไม่เช่นนั้นฉันก็ไม่รู้ว่าหมายถึงอะไร <p> และ? <l> และ?: (กลุ่มการจับภาพที่ไม่จับภาพ) และ \ g <p> และ \ k <l + 0>. ฉันยังมองไม่เห็นอะไร <p> | แม้ว่า ไม่ | หมายความว่า "หรือ"? ฉันไม่พบเอกสารการใช้งานท่อดังกล่าวใน REs ฉันยังคงรู้สึกยินดีที่ได้เห็นคำอธิบายโดยละเอียดสำหรับ RE ที่ดีมากนี้
Kevin Ford เรือดำน้ำ

6

คุณสามารถทำได้โดยไม่ต้องใช้การเรียกซ้ำ:

\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

การสาธิต


1
นี่เป็นคำตอบที่น่าสนใจมากสำหรับคำถาม regex ที่จริงรูปแบบเฉพาะที่ผ่านบางส่วนของการทดสอบของฉัน ขอบคุณสำหรับ Casimir คนนี้ :)
bobble bubble

1
@bobblebubble: ขอบคุณสำหรับการสนับสนุน อย่างที่คุณเห็นฉันแก้ไขคำตอบนี้เมื่อเร็ว ๆ นี้เนื่องจากเวอร์ชันก่อนหน้านี้ไม่ถูกต้อง (เป็นเวลาสามปีน่าเสียดาย)
Casimir et Hippolyte

5

มันง่ายกว่าที่จะทำด้วยการจัดการสตริงมากกว่านิพจน์ทั่วไป:

bool isPalindrome(String s1)

{

    String s2 = s1.reverse;

    return s2 == s1;
}

ฉันตระหนักดีว่านี่ไม่ได้ตอบคำถามสัมภาษณ์จริงๆ แต่คุณสามารถใช้เพื่อแสดงให้เห็นว่าคุณรู้วิธีการทำงานที่ดีขึ้นได้อย่างไรและคุณไม่ใช่ "คนทั่วไปที่ถือค้อนซึ่งมองทุกปัญหาเป็นเหมือนเล็บ .”


ในขณะที่ฉันชอบคำตอบนี้มากฉันคิดว่าคุณจะได้รับคะแนนพิเศษโดยใช้ BreakIterator เพื่อแยกสตริงออกเป็นอักขระภาพอย่างเหมาะสม
Trejkaz

5

นี่คือคำตอบของฉันสำหรับระดับที่ 5 ของ Regex Golf (ชายคนหนึ่งแผน) ใช้งานได้สูงสุด 7 ตัวอักษรด้วย Regexp ของเบราว์เซอร์ (ฉันใช้ Chrome 36.0.1985.143)

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

นี่คือหนึ่งสำหรับอักขระไม่เกิน 9 ตัว

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

หากต้องการเพิ่มจำนวนอักขระสูงสุดที่ใช้ได้คุณจะแทนที่ซ้ำ ๆ? กับ(?: (.).? \ n?)? .


1
ฉันจัดการอันนั้นด้วยอักขระน้อยกว่าเล็กน้อย ^ (.) (.) (.)?.? \ 3 \ 2 \ 1 $
Ben Ellis

ขอบคุณมากสำหรับการสปอยล์ให้ฉัน :-)
ส่งต่อ

ทำไมคนที่เหลือถึงมี 13 คน แต่นี่คือ 19
ส่งต่อ

5

Recursive Regular Expressions ทำได้!

อัลกอริธึมที่เรียบง่ายและชัดเจนในการตรวจจับสตริงที่มี palindrome:

   (\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)$ ทำงานเหมือนกัน แต่เป็นใช่ / ไม่ใช่แทน "มี"
ป.ล. : ใช้คำจำกัดความโดยที่ "o" ไม่ใช่ palimbrome รูปแบบยัติภังค์ "able-elba" ไม่ใช่ palindrome แต่เป็น "canelba" การตั้งชื่อนิยาม 1.
เมื่อ "o" และ "able-elba" เป็น palindrones การตั้งชื่อdefinition2 .

เมื่อเปรียบเทียบกับ "palindrome regexes" อื่น

  • ^((.)(?:(?1)|.?)\2)$base-regex ด้านบนโดยไม่มี\wข้อ จำกัด ยอมรับ "able-elba"

  • ^((.)(?1)?\2|.)$( @LilDevil ) ใช้คำจำกัดความ 2 (ยอมรับ "o" และ "able-elba" ซึ่งแตกต่างกันในการรับรู้สตริง "aaaaa" และ "bbbb")

  • ^((.)(?1)\2|.?)$( @Markus ) ตรวจไม่พบ "kook" หรือ "bbbb"

  • ^((.)(?1)*\2|.?)$( @Csaba ) ใช้คำจำกัดความ 2.


หมายเหตุ: ในการเปรียบเทียบคุณสามารถเพิ่มคำเพิ่มเติมที่$subjectsและบรรทัดสำหรับ regex ที่เปรียบเทียบแต่ละรายการ

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

ฉันลองสิ่งนี้และดูเหมือนว่ามันจะตรงกับ palindromes ทั้งหมด:^((.)(?:(?1)|.?)\2|(.)\3*)$
Hao Wu

4

เกี่ยวกับนิพจน์ PCRE (จาก MizardX):

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

คุณได้ทดสอบหรือไม่? บน PHP 5.3 ของฉันภายใต้ Win XP Pro มันล้มเหลวบน: aaaba จริงๆแล้วฉันแก้ไขนิพจน์นิพจน์เล็กน้อยเพื่ออ่าน:

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

ฉันคิดว่าสิ่งที่เกิดขึ้นคือในขณะที่ตัวละครคู่นอกถูกยึดไว้ แต่ตัวละครชั้นในที่เหลือไม่ได้ นี่ไม่ใช่คำตอบทั้งหมดเพราะในขณะที่ส่งผ่าน "aaaba" และ "aabaacaa" อย่างไม่ถูกต้อง แต่ก็ไม่ถูกต้องใน "aabaaca"

ฉันสงสัยว่ามีการแก้ไขสำหรับสิ่งนี้หรือไม่และตัวอย่าง Perl (โดย JF Sebastian / Zsolt) ผ่านการทดสอบของฉันอย่างถูกต้องหรือไม่

Csaba Gabor จากเวียนนา




3

regex นี้จะตรวจจับ palindromes สูงสุด 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


2

ตามที่ZCHudsonชี้ไว้ให้ตรวจสอบว่าบางสิ่งที่เป็น palindrome ไม่สามารถทำได้ด้วย regexp ตามปกติเนื่องจากชุดของ palindrome ไม่ใช่ภาษาปกติ

ฉันไม่เห็นด้วยกับAirsource Ltdอย่างสิ้นเชิง เมื่อเขาบอกว่า "มันเป็นไปไม่ได้" ไม่ใช่คำตอบที่ผู้สัมภาษณ์ต้องการ ในระหว่างการสัมภาษณ์ของฉันฉันเจอคำถามแบบนี้เมื่อฉันเผชิญหน้ากับผู้สมัครที่ดีเพื่อตรวจสอบว่าเขาสามารถหาข้อโต้แย้งที่ถูกต้องได้หรือไม่เมื่อเราเสนอให้เขาทำอะไรผิด ฉันไม่อยากจ้างคนที่พยายามทำอะไรผิด ๆ ถ้าเขารู้ดีกว่านี้



2

ฉันจะอธิบายให้ผู้สัมภาษณ์เข้าใจว่าภาษาที่ประกอบด้วยภาษาปาลินโดรมไม่ใช่ภาษาปกติ แต่ไม่มีบริบทแทน

การแสดงออกปกติที่จะตรงกับ palindromes ทั้งหมดจะไม่มีที่สิ้นสุด แต่ฉันขอแนะนำให้เขา จำกัด ตัวเองให้มีขนาดสูงสุดของ palindromes ที่จะยอมรับได้ หรือถ้าจำเป็นต้องใช้ palindromes ทั้งหมดให้ใช้ NDPA บางประเภทอย่างน้อยที่สุดหรือใช้เทคนิคการกลับตัว / เท่ากับสตริงอย่างง่าย


2

สิ่งที่ดีที่สุดที่คุณสามารถทำได้กับ regexes ก่อนที่คุณจะหมดกลุ่มการบันทึก:

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

สิ่งนี้จะจับคู่ palindromes ทั้งหมดที่มีความยาวไม่เกิน 19 อักขระ

การแก้โปรแกรมสำหรับความยาวทั้งหมดเป็นเรื่องเล็กน้อย:

str == str.reverse ? true : false

regex ของคุณไม่ทำงาน ตัวอย่างเช่นจะระบุว่า "abac" ตรงกัน ...
Darwin Airola

2

ฉันยังไม่มีตัวแทนที่จะแสดงความคิดเห็นแบบอินไลน์ แต่ regex ที่ MizardX จัดหาให้และแก้ไขโดย Csaba สามารถแก้ไขเพิ่มเติมเพื่อให้ทำงานใน PCRE ได้ ความล้มเหลวเพียงอย่างเดียวที่ฉันพบคือสตริงอักขระเดี่ยว แต่ฉันสามารถทดสอบแยกกันได้

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

หากคุณสามารถทำให้ล้มเหลวในสตริงอื่น ๆ โปรดแสดงความคิดเห็น


2
#!/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";

2

จากทฤษฎีออโตมาตามันเป็นไปไม่ได้ที่จะจับคู่กับความยาวใด ๆ (เนื่องจากต้องใช้หน่วยความจำจำนวนไม่ จำกัด ) แต่มันเป็นไปได้ที่จะจับคู่ภาษาบาลีของความยาวคงที่ บอกว่าเป็นไปได้ที่จะเขียนนิพจน์ทั่วไปที่ตรงกับความยาวภาษาบาลีทั้งหมด <= 5 หรือ <= 6 ฯลฯ แต่ไม่ใช่> = 5 เป็นต้นโดยที่ขอบเขตบนไม่ชัดเจน


2

ใน Ruby คุณสามารถใช้\b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z])\bเพื่อจับคู่คำของ palindrome เช่นa, dad, radar, racecar, and redivider. ps: regex นี้จับคู่เฉพาะคำ palindrome ที่มีตัวอักษรยาวเป็นจำนวนคี่

มาดูกันว่า regex นี้จับคู่กับเรดาร์ได้อย่างไร คำว่า bound \ b ตรงกับจุดเริ่มต้นของสตริง เอ็นจิ้น regex เข้าสู่กลุ่มการจับ "word" [az] จับคู่ r ซึ่งจะถูกเก็บไว้ในสแต็กสำหรับกลุ่มการจับภาพ "letter" ที่ระดับการเรียกซ้ำศูนย์ ตอนนี้เอ็นจิ้น regex เข้าสู่การเรียกซ้ำครั้งแรกของกลุ่ม "word" (? 'letter' [az]) จับคู่และบันทึกในระดับการเรียกซ้ำหนึ่ง regex เข้าสู่การเรียกซ้ำครั้งที่สองของกลุ่ม "word" (? 'letter' [az]) จับ d ที่ระดับการเรียกซ้ำสอง ในระหว่างการเรียกซ้ำสองครั้งถัดไปกลุ่มจะจับ a และ r ที่ระดับสามและสี่ การเรียกซ้ำครั้งที่ห้าล้มเหลวเนื่องจากไม่มีอักขระเหลืออยู่ในสตริงให้ [az] จับคู่ เอนจิ้น regex ต้องย้อนรอย

ตอนนี้เอ็นจิ้น regex ต้องลองใช้ทางเลือกที่สองในกลุ่ม "word" [az] ตัวที่สองใน regex ตรงกับ r สุดท้ายในสตริง ขณะนี้เครื่องยนต์ออกจากการเรียกซ้ำที่ประสบความสำเร็จแล้วโดยจะสำรองหนึ่งระดับไปยังการเรียกซ้ำครั้งที่สาม

หลังจากจับคู่ (& word) เครื่องยนต์จะถึง \ k'letter + 0 ' การอ้างอิงกลับล้มเหลวเนื่องจากเอ็นจิ้น regex ได้มาถึงจุดสิ้นสุดของสตริงหัวเรื่องแล้ว ดังนั้นจึงย้อนรอยอีกครั้ง ทางเลือกที่สองตอนนี้ตรงกับไฟล์. เอ็นจิ้น regex ออกจากการเรียกซ้ำครั้งที่สาม

เอ็นจิ้น regex ได้จับคู่ (& word) อีกครั้งแล้วและจำเป็นต้องพยายามย้อนกลับอีกครั้ง backreference ระบุ +0 หรือระดับปัจจุบันของการเรียกซ้ำซึ่งก็คือ 2 ในระดับนี้กลุ่มการจับจับคู่ d backreference ล้มเหลวเนื่องจากอักขระถัดไปในสตริงคือ r ย้อนกลับอีกครั้งทางเลือกที่สองตรงกับ d.

ตอนนี้ \ k'letter + 0 'จะจับคู่ a ตัวที่สองในสตริง นั่นเป็นเพราะเอ็นจิ้น regex กลับมาถึงการเรียกซ้ำครั้งแรกในระหว่างที่กลุ่มการจับจับคู่กับ a. เอ็นจิ้น regex ออกจากการเรียกซ้ำครั้งแรก

ขณะนี้เอ็นจิ้น regex กลับมาอยู่นอกการเรียกซ้ำทั้งหมดแล้ว ระดับนี้กลุ่มการจับเก็บไว้ r. backreference สามารถจับคู่ r สุดท้ายในสตริงได้แล้ว เนื่องจากเครื่องยนต์ไม่ได้อยู่ในการเรียกซ้ำอีกต่อไปมันจะดำเนินการกับส่วนที่เหลือของ regex หลังจากกลุ่ม \ b จับคู่ที่ส่วนท้ายของสตริง ถึงจุดสิ้นสุดของ regex และเรดาร์จะกลับมาเหมือนการจับคู่โดยรวม


2

นี่คือรหัส PL / SQL ซึ่งจะบอกว่าสตริงที่กำหนดเป็น palindrome หรือไม่ใช้นิพจน์ทั่วไป:

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

0

การปรับแต่งวิธีการของ Airsource Ltd เล็กน้อยในรหัสเทียม:

WHILE string.length > 1
    IF /(.)(.*)\1/ matches string
        string = \2
    ELSE
        REJECT
ACCEPT
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.