Regex: Ghép một loạt bình đẳng


18

Giới thiệu

Tôi không thấy nhiều thách thức regex ở đây, vì vậy tôi muốn đưa ra thử thách đơn giản này có thể được thực hiện theo một số cách sử dụng một số hương vị regex. Tôi hy vọng nó cung cấp cho những người đam mê regex với một chút thời gian chơi golf vui vẻ.

Thử thách

Thách thức là để phù hợp với những gì tôi đã rất lỏng lẻo gọi là một sê-ri "bình đẳng": một loạt các số lượng nhân vật khác nhau bằng nhau. Điều này được mô tả tốt nhất với các ví dụ.

Trận đấu:

aaabbbccc
xyz 
iillppddff
ggggggoooooollllllffffff
abc
banana

Không khớp:

aabc
xxxyyzzz
iilllpppddff
ggggggoooooollllllfff
aaaaaabbbccc
aaabbbc
abbaa
aabbbc

Để khái quát hóa, chúng tôi muốn khớp một chủ đề của biểu mẫu ( cho bất kỳ danh sách các ký tự nào , ở đâu cho tất cảc1)n(c2)n(c3)n...(ck)nc1ckci != ci+1i, k > 1, and n > 0.

Làm rõ:

  • Đầu vào sẽ không trống.

  • Một ký tự có thể lặp lại sau trong chuỗi (ví dụ: "chuối")

  • k > 1, vì vậy sẽ luôn có ít nhất 2 ký tự khác nhau trong chuỗi.

  • Bạn chỉ có thể giả sử các ký tự ASCII sẽ được chuyển làm đầu vào và không có ký tự nào là dấu kết thúc dòng.

Quy tắc

(Cảm ơn Martin Ender vì khối quy tắc được nêu rõ này)

Câu trả lời của bạn nên bao gồm một regex duy nhất, không có bất kỳ mã bổ sung nào (ngoại trừ, tùy chọn, một danh sách các sửa đổi regex cần thiết để làm cho giải pháp của bạn hoạt động). Bạn không được sử dụng các tính năng của hương vị regex của ngôn ngữ cho phép bạn gọi mã bằng ngôn ngữ lưu trữ (ví dụ: công cụ esửa đổi của Perl ).

Bạn có thể sử dụng bất kỳ hương vị regex nào tồn tại trước thử thách này, nhưng vui lòng chỉ định hương vị.

Đừng cho rằng regex được neo hoàn toàn, ví dụ nếu bạn đang sử dụng Python, giả sử rằng regex của bạn được sử dụng với re.search chứ không phải với re.match. Regex của bạn phải khớp với toàn bộ chuỗi cho các chuỗi bình đẳng hợp lệ và không mang lại kết quả khớp nào cho các chuỗi không hợp lệ. Bạn có thể sử dụng nhiều nhóm chụp như bạn muốn.

Bạn có thể giả định rằng đầu vào sẽ luôn là một chuỗi gồm hai hoặc nhiều ký tự ASCII không chứa bất kỳ đầu cuối dòng nào.

Đây là regex golf, vì vậy regex ngắn nhất tính bằng byte thắng. Nếu ngôn ngữ của bạn yêu cầu các dấu phân cách (thông thường /.../) để biểu thị các biểu thức chính quy, đừng đếm chính các dấu phân cách. Nếu giải pháp của bạn yêu cầu sửa đổi, hãy thêm một byte cho mỗi sửa đổi.

Tiêu chí

Đây là môn đánh gôn thời trang tốt, vì vậy hãy quên đi hiệu quả và chỉ cần cố gắng để regex của bạn nhỏ nhất có thể.

Vui lòng đề cập đến hương vị regex nào bạn đã sử dụng và, nếu có thể, bao gồm một liên kết hiển thị bản demo trực tuyến về biểu hiện của bạn trong hành động.


Đây có phải là một sân golf regex? Bạn có lẽ nên làm rõ điều đó, cùng với các quy tắc cho nó. Hầu hết các thách thức trên trang web này là các sân golf của các ngôn ngữ lập trình.
LyricLy

@LyricLy Cảm ơn lời khuyên! Vâng, tôi muốn nó hoàn toàn là regex, tức là. một biểu thức chính quy duy nhất trong một hương vị regex của sự lựa chọn của người gửi. Có bất kỳ quy tắc nào khác tôi nên lưu tâm bao gồm?
jaytea

Tôi không hiểu định nghĩa của bạn về "bình đẳng", như vậy bananalà bình đẳng.
msh210

@ msh210 Khi tôi đưa ra thuật ngữ "egalitarian" để mô tả bộ truyện, tôi đã không cân nhắc rằng tôi sẽ cho phép các nhân vật được lặp lại sau trong loạt bài này (chẳng hạn như trong "chuối", hoặc "aaabbbcccaaa", v.v.) . Tôi chỉ muốn một thuật ngữ để thể hiện ý tưởng rằng mỗi đoạn ký tự lặp lại có cùng kích thước. Vì "chuối" không có ký tự lặp lại, định nghĩa này đúng với nó.
jaytea

Câu trả lời:


11

Hương vị .NET, 48 byte

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

Hãy thử trực tuyến! (sử dụng võng mạc )

Chà, hóa ra việc không phủ nhận logic thì đơn giản hơn. Tôi đang làm điều này thành một câu trả lời riêng biệt, bởi vì hai cách tiếp cận hoàn toàn khác nhau.

Giải trình

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

Tôi thích rằng bạn bập bênh giữa hai phương pháp! Tôi cũng nghĩ rằng cách tiếp cận tiêu cực nên ngắn hơn cho đến khi tôi thực sự thử nó và thấy nó khó xử hơn nhiều (mặc dù cảm giác như nó nên đơn giản hơn). Tôi có 48b trong PCRE và 49b trong Perl với một phương thức hoàn toàn khác và với phương thức thứ 3 của bạn trong .NET có cùng kích thước, tôi nói đây là một thử thách regex khá thú vị: D
jaytea 19/11/17

@jaytea Tôi rất muốn nhìn thấy những thứ đó. Nếu không ai nghĩ ra bất cứ điều gì trong một tuần hoặc lâu hơn, tôi hy vọng bạn sẽ tự đăng chúng. :) Và vâng đồng ý, thật tuyệt khi các cách tiếp cận rất gần với số byte.
Martin Ender

Tôi chỉ có thể! Ngoài ra, Perl one đã được đánh golf xuống 46b;)
jaytea

Vì vậy, tôi hình dung bạn có thể muốn xem những cái này ngay bây giờ! 48B ở đây trong PCRE: ((^.|\2(?=.*\4\3)|\4(?!\3))(?=\2*+((.)\3?)))+\3$Tôi đã thử nghiệm với \3*ở vị trí của (?!\3)để làm cho nó 45B nhưng điều đó không thành công trên "aabbbc" :( Phiên bản Perl là dễ hiểu, và nó xuống 45B bây giờ: ^((?=(.)\2*(.))(?=(\2(?4)?\3)(?!\3))\2+)+\3+$- lý do tôi gọi nó là Perl mặc dù nó PCRE có vẻ hợp lệ là PCRE cho rằng (\2(?4)?\3)có thể tái diễn vô thời hạn trong khi Perl thông minh hơn / tha thứ một chút!
jaytea

@jaytea Ah, đó là những giải pháp thực sự gọn gàng. Bạn thực sự nên đăng chúng trong một câu trả lời riêng biệt. :)
Martin Ender

9

Hương vị .NET, 54 byte

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

Hãy thử trực tuyến! (sử dụng võng mạc )

Tôi khá chắc chắn rằng điều này là tối ưu, nhưng đây là điều tốt nhất tôi nghĩ ra để cân bằng các nhóm ngay bây giờ. Tôi đã có một lựa chọn thay thế ở cùng một số byte, hầu hết đều giống nhau:

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

Giải trình

Ý tưởng chính là đảo ngược vấn đề, khớp các chuỗi không bình đẳng và đặt toàn bộ sự việc trong một cái nhìn tiêu cực để phủ nhận kết quả. Lợi ích là chúng ta không phải theo dõi n trong toàn bộ chuỗi (vì tính chất của các nhóm cân bằng, bạn thường tiêu thụ n khi kiểm tra nó), để kiểm tra xem tất cả các lần chạy có độ dài bằng nhau. Thay vào đó, chúng tôi chỉ tìm kiếm một cặp chạy liền kề không có cùng độ dài. Bằng cách đó, tôi chỉ cần sử dụng n một lần.

Đây là một sự cố của 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.

Tôi cố gắng để làm cho nó thất bại trên: banana, aba, bbbaaannnaaannnaaa, bbbaaannnaaannnaaaaaa, The Nineteenth Byte, 11, 110, ^(?!.*(?<=(\2)*(.))(?!\2)(?>(.)(?<-1>\3)*)(?(1)|\3)).+, bababa. Chính tôi là người thất bại. :( +1
Erik the Outgolfer 19/11/17

1
Khoảnh khắc đó khi bạn hoàn thành lời giải thích của mình và sau đó nhận ra rằng bạn có thể tiết kiệm 1 byte bằng cách sử dụng phương pháp ngược lại chính xác ... Tôi đoán tôi sẽ thực hiện một câu trả lời khác sau một chút ...: |
Martin Ender

@MartinEnder ... Và sau đó nhận ra bạn có thể chơi cái này 2 byte haha: P
Ông Xcoder

@ Mr.Xcoder Bây giờ sẽ phải là 7 byte, vì vậy tôi hy vọng tôi an toàn. ;)
Martin Ender
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.