10
\$\begingroup\$

The TV series "Breaking Bad" replaced the letters Br and Ba with a periodic-table like representation, printing [Br35]eaking [Ba56]d.

Create a program that takes a string input, does a replacement and prints an output. The replacement shall subsidize any substring that matches an element symbol with the notation demonstrated in [Br35]eaking [Ba56]d. That is, add the atomic number to the element symbol and enclose in square brackets.

All elements start with a capital letter and consist of either one or two letters. The highest element to be considered is Og118. From wikipedia:

1 H, 2 He, 3 Li, 4 Be, 5 B, 6 C, 7 N, 8 O, 9 F, 10 Ne, 11 Na, 12 Mg, 13 Al, 14 Si, 15 P, 16 S, 17 Cl, 18 Ar, 19 K, 20 Ca, 21 Sc, 22 Ti, 23 V, 24 Cr, 25 Mn, 26 Fe, 27 Co, 28 Ni, 29 Cu, 30 Zn, 31 Ga, 32 Ge, 33 As, 34 Se, 35 Br, 36 Kr, 37 Rb, 38 Sr, 39 Y, 40 Zr, 41 Nb, 42 Mo, 43 Tc, 44 Ru, 45 Rh, 46 Pd, 47 Ag, 48 Cd, 49 In, 50 Sn, 51 Sb, 52 Te, 53 I, 54 Xe, 55 Cs, 56 Ba, 57 La, 58 Ce, 59 Pr, 60 Nd, 61 Pm, 62 Sm, 63 Eu, 64 Gd, 65 Tb, 66 Dy, 67 Ho, 68 Er, 69 Tm, 70 Yb, 71 Lu, 72 Hf, 73 Ta, 74 W, 75 Re, 76 Os, 77 Ir, 78 Pt, 79 Au, 80 Hg, 81 Tl, 82 Pb, 83 Bi, 84 Po, 85 At, 86 Rn, 87 Fr, 88 Ra, 89 Ac, 90 Th, 91 Pa, 92 U, 93 Np, 94 Pu, 95 Am, 96 Cm, 97 Bk, 98 Cf, 99 Es, 100 Fm, 101 Md, 102 No, 103 Lr, 104 Rf, 105 Db, 106 Sg, 107 Bh, 108 Hs, 109 Mt, 110 Ds, 111 Rg, 112 Cn, 113 Nh, 114 Fl, 115 Mc, 116 Lv, 117 Ts, 118 Og

Additional rules:

  • As this challenge is about compression as much as about code golf, so you have to provide the element list yourself. You must not use any build in periodic tables provided by our language.
  • Work case-sensitive. That means "Breaking Bad" has 2 replacements, "Breaking bad" has one. The input can be arbitrary and won't always follow English grammar. fOoBar shall become f[O8]o[Ba56]r.
  • Search greedy, [He] takes precedence over [H].

Shortest code in bytes wins.

\$\endgroup\$
17
  • 5
    \$\begingroup\$ please 1) add a scoring criterion 2) link the other challenge. \$\endgroup\$ Aug 17, 2020 at 14:40
  • 7
    \$\begingroup\$ I'm guessing you should prioritise 2 character elements first? For example, Hello shouid become [He2]llo, not [H1]ello? \$\endgroup\$
    – Jo King
    Aug 17, 2020 at 14:45
  • 2
    \$\begingroup\$ @petStorm Of course, Mathematica has one as well. \$\endgroup\$
    – Arnauld
    Aug 18, 2020 at 9:28
  • 5
    \$\begingroup\$ @HighlyRadioactive: The string manipulation task itself is trivial, the challenge lies in compression, as tagged. I feel that relying on a built-in periodic table would be a loophole, and I didn't want to randomize atomic numbers just to make sure. \$\endgroup\$ Aug 18, 2020 at 10:43
  • 2
    \$\begingroup\$ @HighlyRadioactive: Please relax. I am not playing against anything, I created a challenge and made sure it's not circumvented in a way that renders it meaningless. "Looking up the result online" is defined as a common loophole, and I draw an analogy to "choosing a language that has this information built in". If you dislike that you cannot use your cool language feature that beats all other languages, just don't participate. You wouldn't enter into a "compress wikipedia" golf with a library containing compressed wikipedia, either. \$\endgroup\$ Aug 18, 2020 at 17:53

5 Answers 5

6
\$\begingroup\$

JavaScript (ES6), 327 bytes

s=>s.replace(RegExp([...a="HHeLiBeBCNOFNeNaMgAlSiPSClArKCaScTiVCrMnFeCoNiCuZnGaGeAsSeBrKrRbSrYZrNbMoTcRuRhPdAgCdInSnSbTeIXeCsBaLaCePrNdPmSmEuGdTbDyHoErTmYbLuHfTaWReOsIrPtAuHgTlPbBiPoAtRnFrRaAcThPaUNpPuAmCmBkCfEsFmMdNoLrRfDbSgBhHsMtDsRgCnNhFlMcLvTsOg".match(/.[a-z]?/g)].sort(s=>-!!s[1]).join`|`,'g'),s=>`[${s+-~a.indexOf(s)}]`)

Try it online!

How?

The data string consists of all element symbols concatenated together, from lowest to highest atomic number.

"HHeLiBeBCNOFNeNaMg...LvTsOg"

We split it into a list a[] of 118 entries with the following regular expression:

 +------> any character (always a capital letter)
 |   +--> optionally followed by a letter in lower case
 | __|_
 |/    \
/.[a-z]?/g

We create a copy of a[], put all single-character elements at the end of the list and join with pipes:

[...a].sort(s => -!!s[1]).join('|')

Which gives:

"Og|Ts|Lv|Mc|Fl|Nh|...|He|H|B|C|N|O|F|P|S|K|V|Y|I|W|U"

We turn this string into a regular expression and apply it to the input string. Each matched sub-string s is replaced with the pattern:

`[${s + -~a.indexOf(s)}]`
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I feel like you'd have tried, but can you use the inverse of atob('HHeLiBeBCNOFNeNaMgAlSiPSClArKCaScTiVCrMnFeCoNiCuZnGaGeAsSeBrKrRbSrYZrNbMoTcRuRhPdAgCdInSnSbTeIXeCsBaLaCePrNdPmSmEuGdTbDyHoErTmYbLuHfTaWReOsIrPtAuHgTlPbBiPoAtRnFrRaAcThPaUNpPuAmCmBkCfEsFmMdNoLrRfDbSgBhHsMtDsRgCnNhFlMcLvTsOg') with btoa as the source string? \$\endgroup\$ Aug 18, 2020 at 10:19
4
\$\begingroup\$

Python 3.8, 454 \$\cdots\$409 385 bytes

Saved a whopping 38 40 45 69 bytes (and fixed a bug) thanks to ovs!!!

eval("lambda s:_]and_<b][-1]".replace('_',"[(s:=re.sub(f'(?<!\[){b}',f'[{b}{e}]',s))for e,b in p if b[1:]"))
import re
p=[*enumerate(re.split("(?=[A-Z])","HHeLiBeBCNOFNeNaMgAlSiPSClArKCaScTiVCrMnFeCoNiCuZnGaGeAsSeBrKrRbSrYZrNbMoTcRuRhPdAgCdInSnSbTeIXeCsBaLaCePrNdPmSmEuGdTbDyHoErTmYbLuHfTaWReOsIrPtAuHgTlPbBiPoAtRnFrRaAcThPaUNpPuAmCmBkCfEsFmMdNoLrRfDbSgBhHsMtDsRgCnNhFlMcLvTsOg"))][1:]

Try it online!

First goes through all two letter chemicals and then single letter ones.

\$\endgroup\$
5
  • \$\begingroup\$ If you use re.split to generate a list of elements, this can be 416 bytes (link without footer). \$\endgroup\$
    – ovs
    Aug 17, 2020 at 17:15
  • \$\begingroup\$ @ovs That's sweet - thanks! :D \$\endgroup\$
    – Noodle9
    Aug 17, 2020 at 17:43
  • \$\begingroup\$ I think you currently have an issue with strings that need a single-letter replacement at the beginning like "Hi!". This can be fixed by adjusting the regex slightly. Here is a fix for this and a suggestion for a few more bytes saved with Python 3.8 ;). \$\endgroup\$
    – ovs
    Aug 17, 2020 at 18:08
  • \$\begingroup\$ @ovs Think the backward search just for the single letter chemicals is all that's needed so we don't double-up on any previous two letter ones - thanks again! :-) \$\endgroup\$
    – Noodle9
    Aug 17, 2020 at 18:18
  • \$\begingroup\$ @ovs Never mind, that brilliant eval solution is shorter than the others and backward searches both times. Was thinking of trying to fold those two for-loops together with eval/replace but this blows my socks off!!! Thanks so much! :D \$\endgroup\$
    – Noodle9
    Aug 17, 2020 at 18:32
4
\$\begingroup\$

Charcoal, 217 bytes

F⪪”}∨"²Q/QH▷⊕Ff←SγG¤º&ρωVφ∨]›¶⁻Nr*Sψ⦄π⁶&U⊞jεJκκH‹ι7◧"↷⌊Rι¦◧◧‽3▷↨↑´^@➙⊙×π+sQ⌈⊙▷TQ|ⅉB{_Π"⪪η(⁵AxQWW/⁻∨8▶u…κ¹*ITλ_⟧‽Hj.⊞;r⬤|›∧7ψjêÞζp⸿⊖¿⊖Q℅↷Hb↨“↔=bY⁵⌈↷¬δ⎚⪫:D₂↓;≦?⁺[‴.t4r±,s^)↗τ”²⊞υΦι›κ ≔⪪⮌S¹θW∧θ⭆⊕№υ⭆²§⮌θκ⊟θ¿№υι«[ιI⊕⌕υι]»ι

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

F⪪”...”²⊞υΦι›κ 

Split a list of element names into pairs of letters and delete the spaces.

≔⪪⮌S¹θ

Reverse the input and split it into characters.

W∧θ⭆⊕№υ⭆²§⮌θκ⊟θ

While there is still input, remove the last 2 characters if there is a matching element, otherwise remove the last character.

¿№υι«

If there is a match in the array, then...

[ιI⊕⌕υι]

...print the match and its atomic number inside []s.

»ι

Otherwise just print the current character.

\$\endgroup\$
3
  • \$\begingroup\$ Looks like this is breaks on spaces, Breaking Bad becomes just [Br35]eaking. \$\endgroup\$
    – Laikoni
    Aug 18, 2020 at 11:18
  • \$\begingroup\$ @Laikoni Charcoal has a weird input format; it first tries to parse it as JSON, then if it's one line it splits on spaces, otherwise it splits on newlines. So if you want the first input to be Breaking Bad you need to pass it as ["Breaking Bad"] or append a trailing newline. \$\endgroup\$
    – Neil
    Aug 18, 2020 at 13:55
  • \$\begingroup\$ Good to know, thanks for the clarification. \$\endgroup\$
    – Laikoni
    Aug 18, 2020 at 14:22
1
\$\begingroup\$

05AB1E, 183 181 bytes

.œʒ.•2вy>ÖΘZθÒ7ßΩ¨ÑÝ
(Îiþ∍ćf=ÆΛ}’.мιoiFδC¸Γ=¢`Ÿíнp»ΛÑzðÿ®ÄÄ‘Â@Âη+(Óûò‘8нKKK#â<Ù#<“râµ5£”м}ÓæuüåÈZµ-ΔÈ;VzeY¯õnK§ÁÚ¡[θƶη„Gp©6›mðÿāε1ΛÎíγJò~܉cT¢Œƶʒ˜•2ô™ðм©såüαP}Σ€g{ygš}θ®DεN>«…[ÿ]}‡J

Pretty slow for long inputs.

Try it online or verify a few more short test cases.

Explanation:

.œ               # Get all partitions of the (implicit) input-string
  ʒ              # Filter these list of parts by:
   .•2вy...ƶʒ˜•  #  Push compressed string "h helibeb c n o f nenamgalsip s clark casctiv crmnfeconicuzngageassebrkrrbsry zrnbmotcrurhpdagcdinsnsbtei xecsbalaceprndpmsmeugdtbdyhoertmybluhftaw reosirptauhgtlpbbipoatrnfrraacthpau nppuamcmbkcfesfmmdnolrrfdbsgbhhsmtdsrgcnnhflmclvtsog"
     2ô          #  Split it into parts of size 2: ["h ","he","li","be","b "...]
       ™         #  Titlecase each string: ["H ","He","Li","Be","B ",...]
        ðм       #  Remove all spaces from each string: ["H","He","Li","Be","B",...]
          ©      #  Store this list in variable `®` (without popping)
           s     #  Swap so the current partition is at the top of the stack
            å    #  Check for each inner part whether it's in the element-list
                 #  (1 if truthy; 0 if falsey)
             ü   #  For each overlapping pair:
              α  #   Get the absolute difference
               P #  Get the product of those to check if all are truthy (1)
                 #  (partitions in the form of [0,1,0,1,...] or [1,0,1,0,...] are left)
  }Σ             # After the filter: sort any remaining partition by:
    €            #  Map each part in the list to:
     g           #   Pop and push its length
      {          #  Sort this list of lengths
       y         #  Push the current partition again
        g        #  Pop and push its length to get the amount of parts in this partition
         š       #  And prepend it at the front of the other lengths
   }θ            # After the sort by: only leave the last partition,
                 # which will have the most parts, as well as the longest individual parts
     ®           # Push the list of elements from variable `®` again
      D          # Duplicate it
       ε         # Map the copy to:
        N>       #  Push the 0-based map index, and increase it by 1
          «      #  Append it to the element-string
           …[ÿ]  #  Push string "[ÿ]", where the `ÿ` is automatically filled with the
                 #  element name and number
       }‡        # After the map: transliterate all elements to the formatted elements in
                 # the partition
         J       # And join it back together to a single string
                 # (after which it is output implicitly as result)

See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•2вy>ÖΘZθÒ7ßΩ¨ÑÝ\n(Îiþ∍ćf=ÆΛ}’.мιoiFδC¸Γ=¢`Ÿíнp»ΛÑzðÿ®ÄÄ‘Â@Âη+(Óûò‘8нKKK#â<Ù#<“râµ5£”м}ÓæuüåÈZµ-ΔÈ;VzeY¯õnK§ÁÚ¡[θƶη„Gp©6›mðÿāε1ΛÎíγJò~܉cT¢Œƶʒ˜• is "h helibeb c n o f nenamgalsip s clark casctiv crmnfeconicuzngageassebrkrrbsry zrnbmotcrurhpdagcdinsnsbtei xecsbalaceprndpmsmeugdtbdyhoertmybluhftaw reosirptauhgtlpbbipoatrnfrraacthpau nppuamcmbkcfesfmmdnolrrfdbsgbhhsmtdsrgcnnhflmclvtsog".

\$\endgroup\$
0
\$\begingroup\$

Perl 5 -p, 313 bytes

map$k{$_}=++$j,HHeLiBeBCNOFNeNaMgAlSiPSClArKCaScTiVCrMnFeCoNiCuZnGaGeAsSeBrKrRbSrYZrNbMoTcRuRhPdAgCdInSnSbTeIXeCsBaLaCePrNdPmSmEuGdTbDyHoErTmYbLuHfTaWReOsIrPtAuHgTlPbBiPoAtRnFrRaAcThPaUNpPuAmCmBkCfEsFmMdNoLrRfDbSgBhHsMtDsRgCnNhFlMcLvTsOg=~/.[a-z]?/g;for$a(sort{$b=~y///c-length$a}keys%k){s/(?<!\[)$a/[$a$k{$a}]/g}

Try it online!

\$\endgroup\$

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