Regex: Eşitlikçi bir seriyi eşleştir


18

Giriş

Burada pek çok regex zorluğu görmüyorum, bu yüzden bir dizi regex aroması kullanarak birkaç şekilde yapılabilen bu aldatıcı basit olanı sunmak istiyorum. Umarım regex meraklılarına eğlenceli golf zamanı sunar.

Meydan okuma

Zorluk, bir "eşitlikçi" dizi olarak çok gevşek olarak adlandırdığım şeyle eşleşmektir: eşit sayıda farklı karakter dizisi. Bu en iyi örneklerle açıklanmaktadır.

Eşleşme:

aaabbbccc
xyz 
iillppddff
ggggggoooooollllllffffff
abc
banana

Eşleşmiyor:

aabc
xxxyyzzz
iilllpppddff
ggggggoooooollllllfff
aaaaaabbbccc
aaabbbc
abbaa
aabbbc

Genelleme, biz (formun bir konu eşleştirmek istediğiniz herhangi bir karakter listesi için için nerede, herkes içinc1)n(c2)n(c3)n...(ck)nc1ckci != ci+1i, k > 1, and n > 0.

Açıklamalar:

  • Giriş boş olmayacak.

  • Bir karakter daha sonra dizede kendini tekrar edebilir (örn. "Muz")

  • k > 1, bu nedenle dizede her zaman en az 2 farklı karakter olacaktır.

  • Giriş olarak yalnızca ASCII karakterlerinin geçirileceğini ve hiçbir karakterin satır sonlandırıcı olmayacağını varsayabilirsiniz.

kurallar

(Bu mükemmel kurallar bloğu için Martin Ender'e teşekkürler)

Cevabınız ek kod olmadan tek bir normal ifadeden oluşmalıdır (isteğe bağlı olarak, çözümünüzü çalıştırabilmeniz için gereken normal ifade değiştiricilerinin bir listesi hariç). Barındıran dilde (örneğin Perl edeğiştiricisi) kodu çağırmanıza izin veren dilinizin regex lezzetinin özelliklerini kullanmamalısınız .

Bu meydan okumadan önce var olan herhangi bir regex aromasını kullanabilirsiniz, ancak lütfen aromayı belirtin.

Normal ifadenin dolaylı olarak sabitlendiğini varsaymayın, örneğin Python kullanıyorsanız normal ifadenizin re.match ile değil re.search ile kullanıldığını varsayın. Normal ifadeniz için normal ifadeniz tüm dizeyle eşleşmeli ve geçersiz dizelerle eşleşmemelidir. İstediğiniz sayıda yakalama grubu kullanabilirsiniz.

Girişin her zaman satır sonlandırıcı içermeyen iki veya daha fazla ASCII karakterden oluşan bir dize olacağını varsayabilirsiniz.

Bu regex golf, yani bayttaki en kısa regex kazanır. /.../Diliniz düzenli ifadeleri belirtmek için (genellikle ) sınırlayıcılar gerektiriyorsa , sınırlayıcıları saymayın. Çözümünüzde değiştirici gerekiyorsa, değiştirici başına bir bayt ekleyin.

Kriterler

Bu moda golf iyi, bu yüzden verimliliği unutma ve sadece regex mümkün olduğunca küçük almaya çalışın.

Lütfen hangi regex lezzetini kullandığınızı belirtin ve mümkünse, ifadenizin çevrimiçi bir demosunu eylemde gösteren bir bağlantı ekleyin.


Bu özellikle normal bir golf mi? Muhtemelen, bununla birlikte kuralları açıklığa kavuşturmalısınız. Bu sitedeki zorlukların çoğu, çeşitli programlama dillerinin golfleridir.
LyricLy

@LyricLy Tavsiye için teşekkürler! Evet, bunun tamamen normal ifade olmasını istiyorum, yani. gönderenin seçiminin normal ifade lezzetinde tek bir düzenli ifade. Eklemem gereken başka kurallar var mı?
jaytea

"Eşitlikçi" tanımınızı anlamıyorum banana, eşitlikçi.
msh210

@ msh210 Diziyi tanımlamak için "eşitlikçi" terimiyle geldiğimde, karakterlerin dizide daha sonra tekrarlanmasına izin vereceğimi düşünmedim ("muz" veya "aaabbbcccaaa" gibi) . Sadece bir terimin, tekrarlanan karakterlerin her bir parçasının aynı boyutta olduğu fikrini temsil etmesini istedim. "Muz" un tekrarlanan karakteri olmadığından, bu tanım bunun için geçerlidir.
17:44

Yanıtlar:


11

.NET aroması, 48 bayt

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

Çevrimiçi deneyin! ( Retina kullanarak )

Eh, çıkıyor değil mantığı negating sonuçta daha basittir. Bunu ayrı bir cevap yapıyorum, çünkü iki yaklaşım tamamen farklı.

açıklama

^            # 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.

İki yöntem arasında tahterevalli yapmayı seviyorum! Ayrıca, olumsuz girişimin gerçekten denemeden ve daha basit (daha basit olmalı gibi hissettirmesine rağmen) bulana kadar daha kısa olması gerektiğini düşündüm. PCRE'de 48b ve tamamen farklı bir yöntemle Perl'de 49b var ve .NET'te aynı boyutta 3. yönteminizle bunun oldukça havalı bir regex meydan okuması olduğunu söyleyebilirim: D
jaytea

@jaytea Bunları görmek isterim. Kimse bir hafta kadar bir şey bulmazsa, umarım bunları kendiniz gönderirsiniz. :) Ve evet, kabul etti, yaklaşımlar bayt sayısında çok yakın.
Martin Ender

Sadece yapabilirim! Ayrıca, Perl bir 46b aşağı golf;)
jaytea

Bunları şimdi görmek isteyebileceğinizi düşündüm! PCRE'nin içinde İşte en 48b: ((^.|\2(?=.*\4\3)|\4(?!\3))(?=\2*+((.)\3?)))+\3$Ben deneme oldu \3*yerine (?!\3)onu 45B yapmak ama bu "aabbbc" konulu başarısız :( Perl versiyonu anlamak daha kolay olduğunu ve şimdi 45B ayrıntıları aşağıda verilmiştir: ^((?=(.)\2*(.))(?=(\2(?4)?\3)(?!\3))\2+)+\3+$- nedeni Hatta bunun bile Perl diyoruz PCRE'nin geçerli olduğu anlaşılıyor PCRE (\2(?4)?\3)süresiz olarak geri çekilebileceğini düşünürken Perl biraz daha akıllı / bağışlayıcı!
jaytea

@jaytea Ah, bunlar gerçekten düzgün çözümler. Onları gerçekten ayrı bir cevapta yayınlamalısınız. :)
Martin Ender

9

.NET aroması, 54 bayt

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

Çevrimiçi deneyin! ( Retina kullanarak )

Eminim ki bu yeterli değildir, ancak şu anda grupları dengelemek için en iyisi budur. Çoğunlukla aynı olan aynı bayt sayısında bir alternatifim var:

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

açıklama

Ana fikir, sorunu tersine çevirmek, eşitlikçi olmayan dizeleri eşleştirmek ve sonucu olumsuzlamak için her şeyi olumsuz bir göz önüne koymaktır. Fayda biz takip etmek zorunda kalmamasıdır n (nedeniyle dengeleme grupların doğası gereği, genellikle tüketmek çünkü tüm dize boyunca n bunu kontrol ederken), tüm geçişler eşit uzunlukta olup olmadığını kontrol etmek. Bunun yerine, aynı uzunlukta olmayan tek bir bitişik çalışma çifti ararız. Bu şekilde, n'yi yalnızca bir kez kullanmam gerekiyor .

İşte normal ifadenin dökümü.

^(?!.*         # 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.

Ben başarısız yapmaya çalıştı: banana, aba, bbbaaannnaaannnaaa, bbbaaannnaaannnaaaaaa, The Nineteenth Byte, 11, 110, ^(?!.*(?<=(\2)*(.))(?!\2)(?>(.)(?<-1>\3)*)(?(1)|\3)).+, bababa. Başarısız olan benim. :( +1
Outgolfer Erik

1
Açıklamanızı bitirip tam tersi yaklaşımı kullanarak 1 bayt tasarruf edebileceğinizi anladığınız an ... Sanırım biraz daha cevap vereceğim ...: |
Martin Ender

@MartinEnder ... Ve sonra bunu 2 bayt haha ​​golf oynayabileceğini anlayın: P
Bay Xcoder

@ Mr.Xcoder 7 bayt olmalı, bu yüzden umarım güvendeyim. ;)
Martin Ender
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.