^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$
Ponieważ jest to tylko jedno wyrażenie regularne, Retina uruchomi się w trybie dopasowania i zgłosi liczbę znalezionych dopasowań, które będą dotyczyć 1
prawidłowych sekwencji i 0
nie tylko. Nie jest to konkurencyjne w porównaniu z językami golfowymi, ale jestem z tego całkiem zadowolony, ponieważ zacząłem od potwora 260 bajtów.
Wyjaśnienie
^((.)(?<!\2.+))*
Ten bit zużywa prefiks unikalnych liter o zmiennej długości, tzn. Pasuje do potencjalnie niekompletnej wiodącej porcji. Lookbehind zapewnia, że żaden znak pasujący do tego bitu nie pojawił się wcześniej w ciągu.
Teraz dla reszty danych wejściowych chcemy dopasować fragmenty po 7 bez powtarzania znaków. Możemy dopasować taki fragment:
(.)(?!.{0,5}\1)(.)(?!.{0,4}\2)(.)(?!.{0,3}\3)...(.)(?!.?\5).
Oznacza to, że dopasowujemy znak, który nie pojawia się dla kolejnych 6 znaków, a następnie taki, który nie pojawia się dla kolejnych 5 znaków i tak dalej. Ale wymaga to dość okropnego powtarzania kodu i musielibyśmy osobno dopasować końcową (potencjalnie niekompletną) część.
Równoważenie grup na ratunek! Inny sposób dopasowania
(.)(?!.{0,5}\1)
polega na wypchnięciu 5 pustych zapałek na stos przechwytywania i spróbuj go opróżnić:
(){5}(.)(?!(?<-1>.)*\2)
*
Pozwala minimum zero powtórzeń, jak {0,5}
i dlatego, że mamy pięć przechwytuje popchnął, nie będą mogli pop więcej niż 5 razy albo. Jest to dłuższe w przypadku pojedynczego wystąpienia tego wzorca, ale jest to o wiele bardziej użyteczne. Ponieważ wyskakujemy z ujemnym wyprzedzeniem, nie ma to wpływu na rzeczywisty stos po zakończeniu przeglądu. Tak więc po lookahead wciąż mamy 5 elementów na stosie, bez względu na to, co się wydarzyło w środku. Ponadto możemy po prostu usunąć jeden element ze stosu przed każdym wyprzedzeniem i uruchomić kod w pętli, aby automatycznie zmniejszyć szerokość wyprzedzenia od 5 do 0. Tak więc naprawdę długi fragment można skrócić do
(){7}((?<-1>)(.)(?!(?<-1>.)*\1\3))*
(Możesz zauważyć dwie różnice: wypychamy 7 zamiast 5. Jedno dodatkowe przechwytywanie polega na tym, że strzelamy przed każdą iteracją, a nie po niej. Druga jest w rzeczywistości konieczna, abyśmy mogli wyskoczyć ze stosu 7 razy (ponieważ chcemy pętla, która ma zostać uruchomiona 7 razy), możemy naprawić ten błąd jeden po drugim w ramach lookahead, upewniając się, \1
że na stosie pozostał co najmniej jeden element.)
Piękno tego polega na tym, że może on również pasować do końcowego niekompletnego fragmentu, ponieważ nigdy nie wymagaliśmy powtarzania go 7 razy (to tylko niezbędne maksimum, ponieważ nie możemy wyskakiwać ze stosu częściej). Więc wszystko, co musimy zrobić, to owinąć to w kolejną pętlę i upewnić się, że doszliśmy do końca łańcucha, aby uzyskać
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$