Võng mạc , 108 102 94 87 82 64 63 byte
Cảm ơn Sp3000 vì đã khiến tôi theo đuổi cách tiếp cận ban đầu của mình, điều này đã đưa số byte từ 108 xuống còn 82.
Rất nhiều lời cảm ơn đến Kobi, người đã tìm ra một giải pháp thanh lịch hơn nhiều, cho phép tôi lưu thêm 19 byte trên đó.
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
.
$0
m+`^(?=( *)\S.*\n\1)
<space>
Trong đó <space>
đại diện cho một ký tự không gian duy nhất (nếu không sẽ bị tước bởi SE). Đối với mục đích đếm, mỗi dòng đi trong một tệp riêng biệt và \n
nên được thay thế bằng một ký tự dòng thực tế. Để thuận tiện, bạn có thể chạy mã như từ một tệp duy nhất có -s
cờ.
Hãy thử trực tuyến.
Giải trình
Chà ... như thường lệ, tôi không thể giới thiệu đầy đủ về các nhóm cân bằng ở đây. Đối với một mồi xem câu trả lời Stack Overflow của tôi .
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
Giai đoạn đầu tiên là giai đoạn S
plit, phân chia đầu vào thành các dòng có chiều dài tăng dần. Dấu _
hiệu cho thấy các khối trống nên được bỏ qua khỏi phần tách (chỉ ảnh hưởng đến phần cuối, bởi vì sẽ có một kết quả khớp ở vị trí cuối cùng). Bản thân regex hoàn toàn được chứa trong một cái nhìn xung quanh nên nó sẽ không khớp với bất kỳ nhân vật nào, mà chỉ có các vị trí.
Phần này dựa trên giải pháp của Kobi với một số sân chơi bổ sung mà tôi tự tìm thấy. Lưu ý rằng lookbehind được khớp từ phải sang trái trong .NET, vì vậy giải thích sau đây tốt nhất nên được đọc từ dưới lên trên. Tôi cũng đã chèn một \G
lời giải thích khác cho rõ ràng, mặc dù điều đó không cần thiết cho mẫu hoạt động.
(?<=
^ # 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.
Tôi vẫn đang ngưỡng mộ công việc của Kobi ở đây. Điều này thậm chí còn thanh lịch hơn so với regex thử nghiệm chính. :)
Hãy chuyển sang giai đoạn tiếp theo:
.
$0
Đơn giản: chèn một khoảng trắng sau mỗi ký tự không phải dòng.
m+`^(?=( *)\S.*\n\1)
<space>
Giai đoạn cuối cùng này thụt lề tất cả các dòng chính xác để tạo thành hình tam giác. Đây m
chỉ là chế độ đa dòng thông thường để ^
khớp với phần đầu của một dòng. Lệnh +
Retina lặp lại giai đoạn này cho đến khi chuỗi ngừng thay đổi (trong trường hợp này có nghĩa là biểu thức chính quy không còn phù hợp nữa).
^ # 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.
)
Vì vậy, điều này khớp với phần đầu của bất kỳ dòng nào không có thụt lề lớn hơn dòng tiếp theo. Trong bất kỳ vị trí như vậy, chúng tôi chèn một không gian. Quá trình này chấm dứt, một khi các dòng được sắp xếp theo hình tam giác gọn gàng, bởi vì đó là bố cục tối thiểu trong đó mỗi dòng có một vết lõm lớn hơn so với dòng tiếp theo.