Regex (.NET), 69 bytes
s/.(?=(.*$)(?<=(?=(?>.*?(.)(?=.*
((?>\3?)(\2))))+\1$)^.*))|
.*/$4/img
Try it online! - all test cases
Try it on regex101! - try it on your own
This is a single regex substitution, to be applied once. This is the only kind of substitution that can be efficiently used on regex101.
Input is taken in the form of the two strings delimited by a newline.
The primary challenge here is that each match+substitution must be done as a separate event, with no communication between them (other than that each subsequent match is done following the end of the previous one). No captures can survive from one match to the next, so each one must reconstruct its state from scratch, relying only on the position of the match to let it do that.
Also note that the actual replacement is only done after all matches are found, so that can't be used to communicate state.
In this explanation, a raw newline is shown as ¶
:
s/ # Match the following:
. # Match a character in string A
(?= # Lookahead (keep this out of what will
# be replaced)
(.*$) # \1 = capture the rest of string A, to
# our position
(?<= # Lookbehind - evaluated from right to
# left, so read from the bottom up
(?= # Lookahead - go back to evaluating from
# left to right.
(?> # Atomic group - after each iteration,
# lock in its match and prevent
# backtracking.
.*? # Skip as few characters as possible,
# minimum zero, to match the following:
(.) # \2 = a character in string A
(?= # Lookahead
.*¶ # Skip to the beginning of string T
( # \3 = concatenation of the following:
(?>\3?) # Previous value of \3, if set
(\2) # $4 = the next character in string T
# (which is capitalized)
)
)
)+ # Loop the above as many times as
# possible, minimum one, to make the
# following match:
\1$ # Stop at the point we snapshotted,
# making sure that the string ends after
# it, to prevent matching an earlier
# duplicate.
)
^.* # First step in the lookbehind - skip
# back to the beginning of the string.
)
)
| # or...
¶.* # Match the newline and string T, in
# order to erase it.
/ # Substitution - replace with this:
$4 # The capital letter we captured in $4,
# which will be empty if it was the
# second alternative that matched.
/ # Flags:
i # Case Insensitive (so that letters in
# string T will match those captured in
# string A)
m # Multiline - "$" matches the end of any
# line instead of just the end of string.
g # Global - replace all matches instead of
# only the first.
Regex (Pythonregex
/ .NET), 74 bytes
s/.(?=(.*$)(?<=(?=(?>.*?(.)(?=.*
(?=(\4?))(\3+(\2))))+\1$)^.*))|
.*/\5/img
Attempt This Online! - Python import regex
Try it online! - .NET
Since mrab-regex supports variable-length lookbehind, the only adaptation necessary in this port was to eliminate the use of nested backreferences. Groups \4
and \5
are copied to each other.
Regex (Perl / PCRE), 86 bytes
s/.(?=(.*)
(.)+((?<=(?=^(?>.*?(.)(?=.*
(\5?+(\4))))+\1
.*(?=\2)\4|(?3)).)))|
.*/$2/sig
Try it online! - Perl - all test cases
Try it online! - PCRE1 - all test cases
Try it online! - PCRE2 v10.33 / Attempt This Online! - PCRE2 v10.40+ - all test cases
Try it on regex101! - PCRE1/PCRE2 - try it on your own
This is a port of the .NET regex, emulating variable-length lookbehind using recursion with fixed-length lookbehind, but there's an additional challenge. The .NET regex makes a capture inside the lookbehind which is used as the substitution replacement. That is impossible in Perl/PCRE, since all captures made inside a subroutine (recursive or not) are erased upon exiting it.
So to communicate back from the emulated lookbehind, this regex makes a guess (\3
, captured by (.)+
) as to what the capital letter in string t
will be, and then compares that with the capital letter actually matched inside the lookbehind ((?=\3)\5
). If it's not a match, the regex will backtrack to before the emulated lookbehind, and try capturing a different capital letter from string t
.
onymiser
substring and syllable count put it just close enough to my username to simulate the uncanny valley effect. \$\endgroup\$