Compactando dados RLE para desenhar arte ASCII

11

Esta pergunta é baseada no que eu vim para responder a outra pergunta .

Às vezes, as perguntas aqui pedem para desenhar alguma arte ASCII. Uma maneira simples de armazenar os dados para a arte é o RLE (codificação em tamanho de execução) . Então:

qqqwwwwweeerrrrrtttyyyy

torna-se:

3q5w3e5r3t4y

Agora, para desenhar uma grande arte ASCII, talvez você esteja obtendo dados como este (ignorando os novos caracteres de linha):

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)

Os caracteres usados ​​para a arte ASCII nunca serão letras ou números em minúsculas ou maiúsculas, apenas sinais, marcas e símbolos, mas sempre no conjunto de caracteres ASCII imprimíveis.

Você deseja economizar espaço nessa sequência, substituindo os números pelo conjunto de caracteres em maiúsculas (sendo 'A' igual a 1, 'B' igual a 2 até 'Z' igual a 26), porque você nunca vai obtenha mais de 26 repetições de um personagem. Então você obtém:

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

(Character count: 34)

E, finalmente, você percebe que alguns grupos de (letra + símbolo) estão se repetindo; portanto, você substitui os grupos que aparecem 3 vezes ou mais na cadeia de caracteres pelo conjunto de caracteres em minúsculas, na ordem ou aparência da cadeia, mas armazenando em um buffer o substituições feitas (no formato "grupo + char de substituição" para cada substituição) e deixando o restante da string como está. Portanto, os seguintes grupos:

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

é substituído por 'a', 'b' e 'c', respectivamente, porque nunca haverá mais de 26 grupos repetindo. Então, finalmente, você obtém:

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

(Character count: 9+24=33)

[A última etapa salva apenas 1 byte porque os grupos que realmente salvam caracteres após serem substituídos são os que aparecem 4 vezes ou mais.]

O desafio

Dada uma sequência que contém os dados do RLE para desenhar uma arte ASCII (com as restrições propostas), escreva o programa / função / método mais curto possível para compactá-lo conforme descrito. O algoritmo deve imprimir / retornar duas strings: a primeira contendo o dicionário usado para a compactação e a segunda sendo a string compactada resultante. Você pode retornar as seqüências de caracteres como uma tupla, uma matriz, uma lista ou qualquer outra coisa, na ordem especificada.

Observe que, se a sequência não puder ser compactada na etapa 2, o algoritmo deverá retornar uma sequência vazia como primeiro valor de retorno e o resultado da etapa 1 como segundo valor de retorno.

Você não precisa incluir o resultado da etapa 1 nos valores de saída, apenas os incluo nos exemplos para fins de esclarecimento.

Este é o , por isso a resposta mais curta para cada idioma ganha!

Outro caso de teste

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:  <empty string>
                         O,O/J$O,O/J"
Charlie
fonte
11
porque você nunca terá mais de 26 repetições de um personagem . aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Okx
@ Ok Esse nunca pode ser o caso.
Erik the Outgolfer
@ Ok, sim, no mundo real. As regras são compostas por um conjunto restrito de arte ASCII, no entanto.
Charlie
2
Em uma implementação real, S,aT bK@cprovavelmente seria armazenado como apenas S,T K@sem nomear explicitamente os caracteres de substituição que podem ser deduzidos trivialmente disso.
precisa
@ Arnauld, você está totalmente certo, eu perdi isso, mas vou deixar a pergunta como está, para o caso de alguém começar a escrever sua resposta.
Charlie

Respostas:

3

JavaScript (ES6), 168 167 bytes

Retorna uma matriz de duas cordas: [dictionary, compressed_string].

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

Casos de teste

Arnauld
fonte
3

Python 2 , 269 280 268 266 bytes

Nada extravagante acontecendo aqui. Boa oportunidade para usar algumas expressões regulares simples.

A primeira versão falhou para cadeias contendo caracteres especiais que foram interpretados na regex. A segunda versão (usando re.escape) funciona com todos os casos de teste. Essa correção custa 11 bytes.

A segunda versão não atribuiu caracteres de substituição em ordem, conforme exigido na especificação do problema e conforme indicado por @CarlosAlejo. Então, de volta à prancheta.

Versão corrigida, mais golfe

  • -6 bytes salvos por não imprimir a saída em duas linhas
  • +3 bytes: alternando para substituições de código por meio de uma string para permitir atender ao desafio, conforme especificado.
  • -4 bytes: como não estou mais chamando re.findall duas vezes, não preciso renomeá-lo
  • -5 bytes: alternando de loops for para loops while.
  • -2 bytes graças a @Comrade Sparkle Pony
import re
S=re.sub
b=a=input()
for i in re.findall('\d{1,2}',a):
 b=S(i, chr(64+int(i)),b)
n,s,p=96,'',0
while p<len(b):
 c=b[p:p+2];f=b.count(c)
 if f>2and not c in s:n+=1;s+=c+chr(n)
 p+=2
p=0
while p<len(s):k=s[p:p+2];v=s[p+2];b=S(re.escape(k),v,b);p+=3
print s,b

Experimente online!

CCB60
fonte
Você está quase chegando, observe que os grupos na segunda etapa não são criados na ordem correta (veja o exemplo). Os grupos devem ser criados em ordem de aparência, portanto o primeiro deve ser O,a.
Charlie
@CarlosAlejo Eu não havia notado isso como requisito, uma vez que as substituições são arbitrárias, do ponto de vista funcional. Os dicionários padrão do Python, uma maneira natural de implementar isso, não são ordenados. Terá de considerar outras estruturas possíveis de dados ....
CCB60
Não foi possível salvar alguns bytes usando b=a=input()e n,s,p=96,'',0?
Camarada SparklePony
\d+seria um regex mais curto para usar. Você nunca ultrapassará 26 de qualquer maneira, portanto não há razão para garantir que seja especificamente de um a dois dígitos. Além disso, usar re.escapesignifica que uma string básica replaceacaba um pouco mais curta: 253 bytes
Value Ink
0

Lua, 215 bytes

Apenas um bom pedaço de correspondência de padrões.

Eu acho que Lua é subestimada quando se trata de golfe ... veja todas essas afirmações juntas!

g,c=string.gsub,string.char
u=g(arg[1],"%d%d?",function(n)return c(n+64)end)l,d=97,""g(u,"..",function(m)n,e=0,g(m,".", "%%%0")g(u,e,function()n=n+1 end)if n>2 then
l,s=l+1,c(l)d,u=d..m..s,g(u,e,s)end
end)print(u,d)
Trebuchette
fonte
0

Python 2 , 186 bytes

from re import*
S=sub('\d+',lambda m:chr(int(m.group(0))+64),input())
Q=[]
for p in findall('[A-Z].',S):
 if S.count(p)>2:a=chr(len(Q)+97);Q+=[p+a];S=sub(escape(p),a,S)
print''.join(Q),S

Eu esperava finalmente encontrar uso para re.subn: C

# first step - convert all numbers to uppercase letters
S=sub('\d+',lambda m:chr(int(m.group(0))+64),input())
# empty list to hold encoding of second step
Q=[]
# find every encoded pair (uppercase letter and some char)
for p in findall('[A-Z].',S):
 # if it occures 3 or move times
 if S.count(p)>2:
  # get lowercase letter to substitute with
  a=chr(len(Q)+97)
  # store encoding into list
  Q+=[p+a]
  # update string - substitute pair with lowercase letter
  S=sub(escape(p),a,S)
# output
# encodings of second step, space, result
# if nothing was compressed at step 2, space would prepend result (of step 1)
print''.join(Q),S

Comprimido na etapa 2

Não compactado na etapa 2


Python 2 , 246 bytes

Todo o segundo passo realizado em repl lambda de re.sub. Apenas por diversão.

from re import*
Q=[]
S=sub('\d+',lambda m:chr(int(m.group(0))+64),input())
S=sub('[A-Z].',lambda m:(lambda m:S.count(m)>2and(m in Q or not Q.append(m))and chr(Q.index(m)+97)or m)(m.group(0)),S)
print''.join(Q[i]+chr(i+97)for i in range(len(Q))),S

Experimente online!

Gambá morto
fonte
0

Perl 5 -pl , 81 bytes

s/\d+/chr$&+64/ge;$b=a;for$a(/([A-Z].)(?=.*\1.*\1)/g){s/\Q$a/$b/g&&($\.=$a.$b++)}

Experimente online!

Imprime a sequência codificada na primeira linha, os triplos na segunda linha

Xcali
fonte
0

Ruby -p , 133 bytes

gsub(/(\d+)(.)/){(64+$1.to_i).chr+$2}
c=?`;s=''
$_.scan(/(..)(?=.*\1.*\1)/){s+=$&+c.succ!if !s[$&]}
puts s.scan(/(..)(.)/){gsub$1,$2}

Experimente online!

Value Ink
fonte