Compressing RLE data to draw ASCII art This question is based on what I came up with to answer another question.


Sometimes the questions here ask to draw some ASCII art. One simple way to store the data for the art is RLE (run-length encoding). So:

qqqwwwwweeerrrrrtttyyyy


becomes:

3q5w3e5r3t4y


Now to draw a big ASCII art you maybe be getting data like this (ignoring the new line characters):

19,20 3(4)11@1$20 11@19,15"4:20 4)19,4:20 11@
   ^^^
   Note that this is "20 whitespaces"

(Character count: 45)


The characters used for the ASCII art are never going to be lowercase or uppercase letters or numbers, only signs, marks and symbols but always in the printable ASCII character set.

You want to save some space in that string, so you replace the numbers with the uppercase character set (being 'A' equals to 1, 'B' equals to 2 until 'Z' equals to 26), because you are never going to get more than 26 repetitions of a character. So you get:

S,T C(D)K@A$T K@S,O"D:T D)S,D:T K@

(Character count: 34)


And finally you notice that some groups of (letter+symbol) are repeating, so you substitute the groups that appear 3 times or more in the string by the lowercase character set, in order or appearance in the string, but storing in a buffer the substitutions made (in the format "group+substitution char" for each substitution), and leaving the rest of the string as is. So the following groups:

S, (3 times) 
T  (4 times)
K@ (3 times)


gets substituted by 'a', 'b' and 'c', respectively, because there are never going to be more than 26 groups repeating. So finally you get:

S,aT bK@c
abC(D)cA$bcaO"D:bD)aD:bc

(Character count: 9+24=33)


[The last step only saves 1 byte because the groups that actually save characters after being substituted are the ones appearing 4 times or more.]

The challenge

Given a string containing the RLE data to draw an ASCII art (with the restrictions proposed), write the shortest program/function/method you can in order to compress it as described. The algorithm must print/return two strings: the first one containing the dictionary used for the compression, and the second one being the resulting compressed string. You may return the strings as a Tuple, an array, List or whatever, in the given order.

Note that if the string cannot be compressed in the step 2, the algorithm must return an empty string as first return value and the result of the step 1 as second return value.

You do not need to include the result of step 1 in the output values, I just include them in the examples for clarification purposes.

This is code-golf, so may the shortest answer for each language win!

Another test case

Input:                   15,15/10$15,15/10"10$10"10$10"10$10"15,15/

Output of step 1:        O,O/J$O,O/J"J$J"J$J"J$J"O,O/

Final algorithm output:  O,aO/bJ$cJ"d
                         abcabdcdcdcdab

---

Input:                   15,15/10$15,15/10"

Output of step 1:        O,O/J$O,O/J"

Final algorithm output:  &lt;empty string&gt;
                         O,O/J$O,O/J"
JavaScript (ES6), 168 167 bytes

Returns an array of two strings: [dictionary, compressed_string].

s=&gt;[(a=(s=s.replace(/\d+/g,n=&gt;C(n|64),C=String.fromCharCode)).match(/../g)).map(v=&gt;s.split(v)[a[v]||3]&gt;=''?D+=v+(a[v]=C(i++)):0,i=97,D='')&amp;&amp;D,a.map(v=&gt;a[v]||v).join``]


Test cases



let f =

s=&gt;[(a=(s=s.replace(/\d+/g,n=&gt;C(n|64),C=String.fromCharCode)).match(/../g)).map(v=&gt;s.split(v)[a[v]||3]&gt;=''?D+=v+(a[v]=C(i++)):0,i=97,D='')&amp;&amp;D,a.map(v=&gt;a[v]||v).join``]

console.log(f('19,20 3(4)11@1$20 11@19,15"4:20 4)19,4:20 11@'))
console.log(f('15,15/10$15,15/10"10$10"10$10"10$10"15,15/'))