18
\$\begingroup\$

let S, a and b each represent a string

Goal: Write a standard string replace function where the you replace all occurrences of a in a string S with b so long as a is not already part of an instance of b

for example, if we have the string S = My oh my that there is a big ol' that

and we wanted to do a fancy replace with a = that and b = that there we would replace every instance of that with that there as long as the instance of that isn't already an instance of that there

So in this case the output would be: My oh my that there is a big ol' that there

The first that is not replaced because it is already part of an instance of that there

Notes

  • All 3 inputs must be strings containing only printable ascii characters

  • Input may be given as 3 separate strings or a list of 3 strings

  • Input will be in the order S, a, b unless otherwise specified in the answer

  • In order for a to be considered a part of b, all of the instance of a must be part of an instance b

Some Corner Cases Explained

Input:  ["1222", "22", "122"]
Output: "12122"

In the case above example the latter 22 is replaced. Even though part of it is part of an instance of b, the entirety of it is NOT a part of the instance of b. Since the entire instance of a is not part of an instance of b it is replaced.

Input:  ["123 ", "23", "12"]
Output: "112 "

This test case illustrates the same case as above but perhaps in a slightly more clear way. Again the 2 in the middle is both part of an instance of a as well as part of an instance of b, however since all of a is not part of the instance of b it is still replaced.

Input: ["Empty", "", "p"]
Output: "pEpmptpyp"

In the above test case both the empty string before and after the p are not replaced as the can wholly be considered part of the instance of p.

Other Test Cases

Input:  ["aabbaa", "aa", "aabb"]
Output: "aabbaabb"

Input:  ["Hello World!", "o", " no"]
Output: "Hell no W norld!"

Input: ["Wow, oh wow, seriously WOW that's... wow", "wow", "WOW,"]
Output: "Wow, oh WOW,, seriously WOW that's... WOW,"

Input: ["Empty", "", "b"]
Output: "bEbmbpbtbyb"

Input: ["Empty", "b", "br"]
Output: "Empty"

Input: ["Empty", "pty", "Empty"]
Output: "Empty"

Input:  ["aabbaaa", "aa", "PP"]
Output: "PPbbPPa"

Input:  ["121212","1","121"]
Output: "121212"

This is a question for code-golf so the shortest answer in bytes wins.

\$\endgroup\$
1

4 Answers 4

6
\$\begingroup\$

Perl 6, 76 bytes

{$^b;$^a;&{S:g/$a<?{$!=$/;all m:ex/$b/>>.&{$!.to>.to||.from>$!.from}}>/$b/}}

Try it online!

Anonymous code block that takes input curried, like f(a,b)(s).

I'm pretty sure this matches up with the intent of the question. Basically, it only makes the substitution if the position of a is not within any of the overlapping matches of b.

\$\endgroup\$
7
  • 2
    \$\begingroup\$ however in tests there's 2p : in pEpmpptpyp instead of pEpmptpyp \$\endgroup\$ Commented Aug 21, 2019 at 19:15
  • \$\begingroup\$ @Nahuel Fixed. This should also handle the other test cases (and I think is the first to do so) \$\endgroup\$
    – Jo King
    Commented Aug 22, 2019 at 9:54
  • \$\begingroup\$ @JoKing Unfortunately after rethinking some of the test cases pointed out, I think the last test case will fail [1222, 22, 122] -> 12122. I have updated the question to clarify and apologize that I hadn't made this corner case more clear earlier. \$\endgroup\$
    – Quinn
    Commented Aug 22, 2019 at 14:19
  • \$\begingroup\$ i had a similar issue with s/(?!$b)$a/$b/g, when $a is empty because empty string after p was matching (?!p) i needed (?<!p)(?!p) \$\endgroup\$ Commented Aug 22, 2019 at 14:23
  • 1
    \$\begingroup\$ @Quinn Fixed I think? \$\endgroup\$
    – Jo King
    Commented Aug 22, 2019 at 23:08
5
\$\begingroup\$

Charcoal, 55 bytes

≔⁰εF⌕AθηF‹‹ιε⬤⌕Aθζ∨‹ικ›⁺ιLη⁺κLζ«≔⁺⁺ω✂θει¹ζω≔⁺ιLηε»⁺ω✂θε

Try it online! Link is to verbose version of code. Explanation:

≔⁰ε

Initialise a variable to show where the last replaced match ended.

F⌕Aθη

Find all overlapping matches of a in S.

F‹‹ιε

If the next match does not overlap the last successful replacement...

⬤⌕Aθζ∨‹ικ›⁺ιLη⁺κLζ«

... and it also overlaps no copy of b in S...

≔⁺⁺ω✂θει¹ζω

... then concatenate the intermediate substring between the last match and this match with b to the output string...

≔⁺ιLηε

... and update the last match end variable to the end of this new match.

»⁺ω✂θε

At the end, add on any remainder of S and output the result.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ @tsh OK this is a complete rewrite, I hope this now covers all possible cases... \$\endgroup\$
    – Neil
    Commented Aug 22, 2019 at 22:41
  • 1
    \$\begingroup\$ @Neil I believe this is also valid ! \$\endgroup\$
    – Quinn
    Commented Aug 23, 2019 at 1:24
  • \$\begingroup\$ @Neil I believe this is valid now. \$\endgroup\$
    – tsh
    Commented Aug 23, 2019 at 2:09
  • \$\begingroup\$ @Quinn Ooh, does that make me first to make their answer valid? Neat! \$\endgroup\$
    – Neil
    Commented Aug 23, 2019 at 9:23
  • \$\begingroup\$ @Neil I believe JoKing also has a valid answer \$\endgroup\$
    – Quinn
    Commented Aug 23, 2019 at 12:49
3
\$\begingroup\$

Wolfram Language (Mathematica), 43 122 96 88 bytes

##2~StringReplacePart~Cases[#2~P~#,{a_,b_}/;And@@(#2<b||#>a&@@@P@##2)]&
P=StringPosition

Try it online!

+79: should be fixed.

Call as f[a,S,b].

                                                                      & (* a function which finds *)
                            #2~P~#,                                     (* the positions {start,end} where a occurs in S *)
                      Cases[       {a_,b_}/;And@@(#2<b||#>a&@@@     )]  (* which are not a subrange of any of the *)
                                                               P@##2    (* positions of b in S, *)
##2~StringReplacePart~                                                  (* and replaces those parts of the string with b *)
P=StringPosition
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Fails for 121212, 1, 121 - should output 121212 \$\endgroup\$
    – Falco
    Commented Aug 22, 2019 at 13:17
  • \$\begingroup\$ @attinat Seems fixed to me! \$\endgroup\$
    – Quinn
    Commented Aug 23, 2019 at 12:49
1
\$\begingroup\$

Perl 5 (-lpF/;/), 41 bytes

($_,$a,$b)=@F;s/(?<!(?=$b).)(?!$b)$a/$b/g

TIO

\$\endgroup\$
6
  • \$\begingroup\$ [1222, 22, 122] should output 12122, but yours outputs 11222 \$\endgroup\$
    – Quinn
    Commented Aug 22, 2019 at 14:23
  • \$\begingroup\$ ok didn't understand that, maybe fixed, and shorter \$\endgroup\$ Commented Aug 22, 2019 at 16:14
  • \$\begingroup\$ Oh no actually the last test case has one too many 12s, turns out the question I made was much harder than I originally thought! \$\endgroup\$
    – Quinn
    Commented Aug 22, 2019 at 16:17
  • \$\begingroup\$ @Quinn, it seems to me that it's not consistent : 123,23,12 -> 112 but 1212,1,121->1212 (? why it shouldn't be 121212) \$\endgroup\$ Commented Aug 23, 2019 at 9:00
  • \$\begingroup\$ it seems to be what should be clarified is which positions in S should not match with b or which part to skip before looking for next match \$\endgroup\$ Commented Aug 23, 2019 at 9:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.