Regex: Cocokkan seri egaliter


18

pengantar

Saya tidak melihat banyak tantangan regex di sini, jadi saya ingin menawarkan ini sederhana yang menipu yang dapat dilakukan dalam beberapa cara menggunakan sejumlah rasa regex. Saya harap ini memberi para penggemar regex waktu bermain golf yang menyenangkan.

Tantangan

Tantangannya adalah untuk mencocokkan apa yang secara longgar saya juluki seri "egaliter": serangkaian jumlah karakter yang berbeda dengan jumlah yang sama. Ini paling baik dijelaskan dengan contoh.

Pertandingan:

aaabbbccc
xyz 
iillppddff
ggggggoooooollllllffffff
abc
banana

Jangan cocokkan:

aabc
xxxyyzzz
iilllpppddff
ggggggoooooollllllfff
aaaaaabbbccc
aaabbbc
abbaa
aabbbc

Untuk menggeneralisasikan, kami ingin mencocokkan subjek dari bentuk ( untuk daftar karakter untuk , di mana untuk semuac1)n(c2)n(c3)n...(ck)nc1ckci != ci+1i, k > 1, and n > 0.

Klarifikasi:

  • Masukan tidak akan kosong.

  • Suatu karakter dapat terulang kembali nanti dalam string (mis. "Pisang")

  • k > 1, jadi akan selalu ada setidaknya 2 karakter berbeda dalam string.

  • Anda dapat mengasumsikan hanya karakter ASCII yang akan diteruskan sebagai input dan tidak ada karakter yang akan menjadi terminator garis.

Aturan

(Terima kasih kepada Martin Ender untuk blok peraturan yang dinyatakan dengan sangat baik ini)

Jawaban Anda harus terdiri dari satu regex, tanpa kode tambahan (kecuali, secara opsional, daftar pengubah regex yang diperlukan untuk membuat solusi Anda berfungsi). Anda tidak boleh menggunakan fitur rasa regex bahasa Anda yang memungkinkan Anda untuk memohon kode dalam bahasa hosting (mis. ePengubah Perl ).

Anda dapat menggunakan rasa regex apa pun yang ada sebelum tantangan ini, tetapi mohon tentukan rasanya.

Jangan berasumsi bahwa regex berlabuh secara implisit, misalnya jika Anda menggunakan Python, anggap bahwa regex Anda digunakan dengan re.search dan bukan dengan re.match. Regex Anda harus cocok dengan seluruh string untuk string egaliter yang valid dan tidak menghasilkan yang cocok untuk string yang tidak valid. Anda dapat menggunakan sebanyak mungkin grup penangkap sesuai keinginan.

Anda dapat mengasumsikan bahwa input akan selalu berupa string dua atau lebih karakter ASCII yang tidak mengandung terminator garis apa pun.

Ini adalah golf regex, jadi regex terpendek yang menang. Jika bahasa Anda membutuhkan pembatas (biasanya /.../) untuk menunjukkan ekspresi reguler, jangan hitung pembatas itu sendiri. Jika solusi Anda memerlukan pengubah, tambahkan satu byte per pengubah.

Kriteria

Ini golf yang bagus, jadi lupakan efisiensi dan cobalah untuk mendapatkan regex sekecil mungkin.

Sebutkan rasa regex mana yang telah Anda gunakan dan, jika mungkin, sertakan tautan yang menunjukkan demo online ekspresi Anda saat beraksi.


Apakah ini khusus golf regex? Anda mungkin harus menjelaskan itu, bersama dengan aturan untuk itu. Sebagian besar tantangan di situs ini adalah berbagai macam bahasa pemrograman.
LyricLy

@LyricLy Terima kasih atas sarannya! Ya, saya ingin itu murni regex, yaitu. satu ekspresi reguler dalam rasa regex pilihan pengirim. Apakah ada aturan lain yang harus saya perhatikan?
jaytea

Saya tidak mengerti definisi Anda tentang "egaliter", sehingga bananaegaliter.
msh210

@ msh210 Ketika saya datang dengan istilah "egaliter" untuk menggambarkan seri, saya tidak menganggap bahwa saya akan mengizinkan karakter untuk diulang nanti dalam seri (seperti dalam "pisang", atau "aaabbbcccaaa", dll.) . Saya hanya ingin istilah untuk mewakili gagasan bahwa setiap potongan karakter yang diulang adalah ukuran yang sama. Karena "pisang" tidak memiliki karakter berulang, definisi ini berlaku untuk itu.
jaytea

Jawaban:


11

.NET rasa, 48 byte

^(.)\1*((?<=(\5())*(.))(.)(?<-4>\6)*(?!\4|\6))+$

Cobalah online! (menggunakan Retina )

Ya, ternyata tidak meniadakan logika itu lebih sederhana. Saya membuat ini sebagai jawaban yang terpisah, karena kedua pendekatan tersebut sangat berbeda.

Penjelasan

^            # Anchor the match to the beginning of the string.
(.)\1*       # Match the first run of identical characters. In principle, 
             # it's possible that this matches only half, a quarter, an 
             # eighth etc of of the first run, but that won't affect the 
             # result of the match (in other words, if the match fails with 
             # matching this as the entire first run, then backtracking into
             # only matching half of it won't cause the rest of the regex to
             # match either).
(            # Match this part one or more times. Each instance matches one
             # run of identical letters.
  (?<=       #   We start with a lookbehind to record the length
             #   of the preceding run. Remember that the lookbehind
             #   should be read from the bottom up (and so should
             #   my comments).
    (\5())*  #     And then we match all of its adjacent copies, pushing an
             #     empty capture onto stack 4 each time. That means at the
             #     end of the lookbehind, we will have n-1 captures stack 4, 
             #     where n is the length of the preceding run. Due to the 
             #     atomic nature of lookbehinds, we don't have to worry 
             #     about backtracking matching less than n-1 copies here.
    (.)      #     We capture the character that makes up the preceding
             #     run in group 5.
  )
  (.)        #   Capture the character that makes up the next run in group 6.
  (?<-4>\6)* #   Match copies of that character while depleting stack 4.
             #   If the runs are the same length that means we need to be
             #   able to get to the end of the run at the same time we
             #   empty stack 4 completely.
  (?!\4|\6)  #   This lookahead ensures that. If stack 4 is not empty yet,
             #   \4 will match, because the captures are all empty, so the
             #   the backreference can't fail. If the stack is empty though,
             #   then the backreference will always fail. Similarly, if we
             #   are not at the end of the run yet, then \6 will match 
             #   another copy of the run. So we ensure that neither \4 nor
             #   \6 are possible at this position to assert that this run
             #   has the same length das the previous one.
)+
$            # Finally, we make sure that we can cover the entire string
             # by going through runs of identical lengths like this.

Saya suka bahwa Anda melihat antara dua metode! Saya juga berpikir pendekatan negatif seharusnya lebih pendek sampai saya benar-benar mencobanya dan merasa jauh lebih canggung (walaupun rasanya lebih sederhana). Saya memiliki 48b di PCRE, dan 49b di Perl dengan metode yang sama sekali berbeda, dan dengan metode 3 Anda di NET sekitar ukuran yang sama, saya akan mengatakan ini adalah cukup tantangan keren regex: D
jaytea

@ jaytea Saya ingin sekali melihatnya. Jika tidak ada yang menemukan sesuatu selama seminggu atau lebih, saya harap Anda akan mempostingnya sendiri. :) Dan ya setuju, itu bagus bahwa pendekatannya sangat dekat dalam hitungan byte.
Martin Ender

Saya mungkin saja! Juga, Perl satu telah golf ke 46b;)
jaytea

Jadi saya pikir Anda mungkin ingin melihatnya sekarang! Inilah 48b dalam PCRE: ((^.|\2(?=.*\4\3)|\4(?!\3))(?=\2*+((.)\3?)))+\3$Saya sedang bereksperimen dengan \3*tempat (?!\3)untuk membuatnya 45b tetapi gagal pada "aabbbc" :( Versi Perl lebih mudah dimengerti, dan sekarang turun menjadi 45b: ^((?=(.)\2*(.))(?=(\2(?4)?\3)(?!\3))\2+)+\3+$- alasan saya menyebutnya Perl meskipun itu tampaknya PCRE valid adalah bahwa PCRE berpikir (\2(?4)?\3)dapat muncul kembali tanpa batas sedangkan Perl sedikit lebih pintar / memaafkan
jaytea

@ Jayta Ah, itu adalah solusi yang sangat rapi. Anda harus benar-benar mempostingnya dalam jawaban terpisah. :)
Martin Ender

9

.NET rasa, 54 byte

^(?!.*(?<=(\2)*(.))(?!\2)(?>(.)(?<-1>\3)*)(?(1)|\3)).+

Cobalah online! (menggunakan Retina )

Saya cukup yakin ini suboptimal, tapi ini yang terbaik yang saya lakukan untuk menyeimbangkan grup saat ini. Saya punya satu alternatif pada jumlah byte yang sama, yang sebagian besar sama:

^(?!.*(?<=(\3())*(.))(?!\3)(?>(.)(?<-2>\4)*)(\2|\4)).+

Penjelasan

Gagasan utamanya adalah membalikkan masalah, mencocokkan string non-egaliter dan menempatkan semuanya dalam tampilan negatif untuk meniadakan hasilnya. Keuntungannya adalah kita tidak harus melacak n di seluruh string (karena sifat dari kelompok penyeimbang, Anda biasanya mengonsumsi n saat memeriksanya), untuk memeriksa bahwa semua run memiliki panjang yang sama. Sebagai gantinya, kami hanya mencari satu pasangan lintasan yang berdekatan yang tidak memiliki panjang yang sama. Dengan begitu, saya hanya perlu menggunakan n sekali.

Berikut ini rincian dari regex.

^(?!.*         # This negative lookahead means that we will match
               # all strings where the pattern inside the lookahead
               # would fail if it were used as a regex on its own.
               # Due to the .* that inner regex can match from any
               # position inside the string. The particular position
               # we're looking for is between two runs (and this
               # will be ensured later).

  (?<=         #   We start with a lookbehind to record the length
               #   of the preceding run. Remember that the lookbehind
               #   should be read from the bottom up (and so should
               #   my comments).
    (\2)*      #     And then we match all of its adjacent copies, capturing
               #     them separately in group 1. That means at the
               #     end of the lookbehind, we will have n-1 captures
               #     on stack 1, where n is the length of the preceding
               #     run. Due to the atomic nature of lookbehinds, we
               #     don't have to worry about backtracking matching
               #     less than n-1 copies here.
    (.)        #     We capture the character that makes up the preceding
               #     run in group 2.
  )
  (?!\2)       #   Make sure the next character isn't the same as the one
               #   we used for the preceding run. This ensures we're at a
               #   boundary between runs.
  (?>          #   Match the next stuff with an atomic group to avoid
               #   backtracking.
    (.)        #     Capture the character that makes up the next run
               #     in group 3.
    (?<-1>\3)* #     Match as many of these characters as possible while
               #     depleting the captures on stack 1.
  )
               #   Due to the atomic group, there are three two possible
               #   situations that cause the previous quantifier to stopp
               #   matching. 
               #   Either the run has ended, or stack 1 has been depleted.
               #   If both of those are true, the runs are the same length,
               #   and we don't actually want a match here. But if the runs
               #   are of different lengths than either the run ended but
               #   the stack isn't empty yet, or the stack was depleted but
               #   the run hasn't ended yet.
  (?(1)|\3)    #   This conditional matches these last two cases. If there's
               #   still a capture on stack 1, we don't match anything,
               #   because we know this run was shorter than the previous
               #   one. But if stack 1, we want to match another copy of 
               #   the character in this run to ensure that this run is 
               #   longer than the previous one.
)
.+             # Finally we just match the entire string to comply with the
               # challenge spec.

Aku mencoba untuk membuatnya gagal pada: banana, aba, bbbaaannnaaannnaaa, bbbaaannnaaannnaaaaaa, The Nineteenth Byte, 11, 110, ^(?!.*(?<=(\2)*(.))(?!\2)(?>(.)(?<-1>\3)*)(?(1)|\3)).+, bababa. Akulah yang gagal. :( +1
Erik the Outgolfer

1
Saat itu ketika Anda menyelesaikan penjelasan Anda dan kemudian mencari tahu bahwa Anda dapat menyimpan 1 byte dengan menggunakan pendekatan yang berlawanan persis ... Saya kira saya akan membuat jawaban lain dalam sedikit ...: |
Martin Ender

@ MartinEnder ... Dan kemudian sadari Anda bisa bermain golf ini dengan 2 byte haha: P
Mr. Xcoder

@ Mr.Xcoder Harus 7 byte sekarang, jadi saya harap saya aman. ;)
Martin Ender
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.