Retina , 108 102 94 87 82 64 63 bajty
Dzięki Sp3000 za zmuszenie mnie do kontynuowania mojego oryginalnego podejścia, co spowodowało zmniejszenie liczby bajtów ze 108 do 82.
Ogromne podziękowania dla Kobi, który znalazł znacznie bardziej eleganckie rozwiązanie, które pozwoliło mi zaoszczędzić kolejne 19 bajtów.
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
.
$0
m+`^(?=( *)\S.*\n\1)
<space>
Gdzie <space>
reprezentuje pojedynczy znak spacji (który w innym przypadku zostałby usunięty przez SE). Do celów zliczania każda linia przechodzi do osobnego pliku i \n
powinna zostać zastąpiona rzeczywistym znakiem linii. Dla wygody możesz uruchomić kod z jednego pliku z -s
flagą.
Wypróbuj online.
Wyjaśnienie
Cóż ... jak zwykle nie mogę tutaj podać pełnego opisu grup równoważących. Aby uzyskać podkład, zobacz moją odpowiedź Przepełnienie stosu .
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
Pierwszy etap to etap S
plitowania, który dzieli wkład na linie o coraz większej długości. _
Wskazuje, że puste kawałki powinny być pominięte z podziału (który tylko wpływa na koniec, bo nie będzie to mecz w ostatniej pozycji). Sam regex jest całkowicie zawarty w rozejrzeniu, więc nie będzie pasował do żadnych znaków, a jedynie do pozycji.
Ta część oparta jest na rozwiązaniu Kobi z pewną dodatkową golfistą, którą znalazłem. Zauważ, że lookbehinds są dopasowane od prawej do lewej w .NET, więc poniższe wyjaśnienie najlepiej jest przeczytać od dołu do góry. Do \G
wyjaśnienia dodałem także inne, chociaż nie jest to konieczne, aby wzorzec zadziałał.
(?<=
^ # And we ensure that we can reach the beginning of the stack by doing so.
# The first time this is possible will be exactly when tri(m-1) == tri(n-1),
# i.e. when m == n. Exactly what we want!
(?<-1>.)* # Now we keep matching individual characters while popping from group <1>.
\G # We've now matched m characters, while pushing i-1 captures for each i
# between 1 and m, inclusive. That is, group <1> contains tri(m-1) captures.
(?:
(?<=
\G # The \G anchor matches at the position of the last match.
(.)* # ...push one capture onto group <1> for each character between here
# here and the last match.
) # Then we use a lookahead to...
. # In each iteration we match a single character.
)+ # This group matches all the characters up to the last match (or the beginning
# of the string). Call that number m.
) # If the previous match was at position tri(n-1) then we want this match
# to happen exactly n characters later.
Nadal podziwiam tu pracę Kobiego. Jest to nawet bardziej eleganckie niż regex pierwszego testu. :)
Przejdźmy do następnego etapu:
.
$0
Proste: wstaw spację po każdym znaku nieliniowym.
m+`^(?=( *)\S.*\n\1)
<space>
Ten ostatni etap prawidłowo wcina wszystkie linie, tworząc trójkąt. Jest m
to zwykły tryb multilinii, który ^
dopasowuje początek linii. +
Mówi Retina powtórzyć ten etap aż łańcuch zatrzyma się zmienia (co w tym przypadku oznacza, że regex nie pasuje już).
^ # Match the beginning of a line.
(?= # A lookahead which checks if the matched line needs another space.
( *) # Capture the indent on the current line.
\S # Match a non-space character to ensure we've got the entire indent.
.*\n # Match the remainder of the line, as well as the linefeed.
\1 # Check that the next line has at least the same indent as this one.
)
To pasuje do początku dowolnej linii, która nie ma większego wcięcia niż następna. W każdej takiej pozycji wstawiamy spację. Proces ten kończy się, gdy linie zostaną ułożone w czysty trójkąt, ponieważ jest to minimalny układ, w którym każda linia ma większe wcięcie niż następna.