+`^((.)(.*¶)*)((.)*\2.*¶)((?<-5>.)*(?(5)\2|(.)\2).*)
$1$#7$* $4$#5$* $6
Try it online! Note: This leaves the alignment character in the output; it can be deleted at a cost of 4 bytes. If only two strings need to be aligned, then for 52 bytes:
^(.)¶((.)*\1.*¶)((?<-3>.)*(.)*\1.*)
$#5$* $2$#3$* $4
Explanation:
^(.)¶
This matches the alignment character.
((.)*\1.*¶)
This matches the first line and also keeps track of how many characters there were before the alignment character. (.NET keeps a match stack for each variable, in this case, $3
.)
((?<-3>.)*(.)*\1.*)
This matches the second line, trying to account for as many characters as we found on the first line. ?<-3>
causes the match to pop the stack for each character, until it is empty, at which point the match fails, and the (.)*
then matches the remaining characters before the alignment character. At this point we have the following variables:
$1
contains the alignment character
$2
contains the first line
$3
contains a stack whose length is the first line prefix minus the second line prefix
$4
contains the second line
$5
contains a stack whose length is the second line prefix minus the first line prefix
$#5$*
then prefixes the necessary number of spaces to make the first line align with the second, and vice versa for $#3$*
.
Similar logic applies to the main answer, except here we have to find two lines which don't align so that we can align them (this is where the ?(5)
comes in) and then repeat the alignment over all the lines until they are all equally aligned.