Analisar o formato de dicionário Bookworm

42

Recentemente, tenho me entregado a alguma nostalgia na forma de Bookworm Deluxe:

Caso você nunca tenha visto isso antes, é um jogo de palavras em que o objetivo é conectar peças adjacentes para formar palavras. Para determinar se uma string é uma palavra válida, ela é comparada com seu dicionário interno, que é armazenado em um formato compactado com a seguinte aparência:

aa
2h
3ed
ing
s
2l
3iis
s
2rdvark
8s
4wolf
7ves

As regras para descompactar o dicionário são simples:

  1. Leia o número no início da linha e copie esses caracteres desde o início da palavra anterior. (Se não houver número, copie o número de caracteres que você fez da última vez.)

  2. Acrescente as seguintes letras à palavra.

Portanto, nossa primeira palavra é aa, seguida por 2h, que significa "copiar as duas primeiras letras de aae acrescentar h", formando aah. Então 3edse torna aahede, como a próxima linha não tem um número, copiamos três caracteres novamente para formar aahing. Esse processo continua no restante do dicionário. As palavras resultantes da pequena entrada de amostra são:

aa
aah
aahed
aahing
aahs
aal
aaliis
aals
aardvark
aardvarks
aardwolf
aardwolves

Seu desafio é realizar essa descompactação no menor número de bytes possível.

Cada linha de entrada conterá zero ou mais dígitos 0-9 seguidos por uma ou mais letras minúsculas a-z. Você pode receber e fornecer saída como uma lista de cadeias ou como uma única cadeia com palavras separadas por qualquer caractere que não seja 0-9/ a-z.

Aqui está outro pequeno caso de teste com alguns casos extremos não abordados no exemplo:

abc cba 1de fg hi 0jkl mno abcdefghijk 10l
=> abc cba cde cfg chi jkl mno abcdefghijk abcdefghijl

Você também pode testar seu código no dicionário completo: entrada , saída .

Maçaneta da porta
fonte
Existe a possibilidade de que não haja um número na segunda linha? Além disso, podemos assumir que nenhum número, exceto 0, terá 0s iniciais ?
Erik the Outgolfer
@EriktheOutgolfer Sim, isso é possível; Eu adicionei isso ao caso de teste. E sim, você pode supor que (assim como o número não será maior que o comprimento da palavra anterior).
Maçaneta
11
Esse é um formato bonito de compactação:]
Poke
1
O locateprograma usa esse tipo de codificação em nomes de caminho.
Dan D.
Eu escrevi este programa para meu uso real, há cerca de 15 anos. Infelizmente eu não acho que eu tenho a fonte mais ...
hobbs

Respostas:

10

JavaScript (ES6),  66 62  61 bytes

a=>a.map(p=s=>a=a.slice([,x,y]=/(\d*)(.*)/.exec(s),p=x||p)+y)

Experimente online!

Comentado

a =>                  // a[] = input, re-used to store the previous word
  a.map(p =           // initialize p to a non-numeric value
  s =>                // for each string s in a[]:
    a =               //   update a:
      a.slice(        //     extract the correct prefix from the previous word:
        [, x, y] =    //       load into x and y:
          /(\d*)(.*)/ //         the result of a regular expression which splits the new
          .exec(s),   //         entry into x = leading digits and y = trailing letters
                      //       this array is interpreted as 0 by slice()
        p = x || p    //       update p to x if x is not an empty string; otherwise leave
                      //       it unchanged; use this as the 2nd parameter of slice()
      )               //     end of slice()
      + y             //     append the new suffix
  )                   // end of map()
Arnauld
fonte
5

Perl 6 , 50 bytes

-2 bytes graças a nwellnhof

{my$l;.map:{$!=S[\d*]=substr $!,0,$l [R||]=~$/}}

Experimente online!

Um porto da solução de Arnauld . Cara, esse R||truque foi uma montanha-russa de 'Eu acho que isso é possível', 'nah, é impossível', 'meio que talvez seja possível' e finalmente 'aha!'

Explicação:

{my$l;.map:{$!=S[\d*]=substr $!,0,$l [R||]=~$/}}
{                                              }  # Anonymous code block
 my$l;    # Declare the variable $l, which is used for the previous number
      .map:{                                  }  # Map the input list to
            $!=              # $! is used to save the previous word
               S[\d*]=       # Substitute the number for
                      substr $!,0    # A substring of the previous word
                                 ,              # With the length of 
                                           ~$0     # The num if it exists
                                  $l [R||]=        # Otherwise the previous num

A $l [R||]=~$/parte é traduzida aproximadamente para $l= ~$/||+$lmas ... tem a mesma quantidade de bytes :(. Originalmente, ele salvava bytes usando uma variável anônima, para my$lque o mapcódigo desaparecesse, mas isso não funciona, já que o escopo agora é a substituição, não o codeblock. Ah bem. De qualquer forma, Ré o metaoperador reverso, portanto, inverte os argumentos de ||, portanto, a $lvariável acaba sendo atribuída ao novo número ( ~$/) se existir, caso contrário, ela mesma novamente.

Pode ter 47 bytes se o Perl 6 não gerar um erro de compilador meio redundante =~.

Brincadeira
fonte
5

Ruby , 49 45 43 bytes

$0=$_=$0[/.{0#{p=$_[/\d+/]||p}}/]+$_[/\D+/]

Experimente online!

Explicação

$0=                                         #Previous word, assign the value of
   $_=                                      #Current word, assign the value of
      $0[/.{0#{              }}/]           #Starting substring of $0 of length p which is
               p=$_[/\d+/]||p               #defined as a number in the start of $_ if any 
                                 +$_[/\D+/] #Plus any remaining non-digits in $_
Kirill L.
fonte
5

C, 65 bytes 57

n;f(){char c[99];while(scanf("%d",&n),gets(c+n))puts(c);}

Experimente online!

Explicação:

n;                     /* n is implicitly int, and initialized to zero. */

f() {                  /* the unpacking function. */

    char c[99];        /* we need a buffer to read into, for the longest line in
                          the full dictionary we need 12 + 1 bytes. */

    while(             /* loop while there is input left. */

        scanf("%d",&n) /* Read into n, if the read fails because this line
                          doesn't have a number n's value does not change.
                          scanf's return value is ignored. */

        ,              /* chain expressions with the comma operator. The loop
                          condition is on the right side of the comma. */

        gets(c+n))     /* we read into c starting from cₙ. c₀, c₁.. up to cₙ is
                          the shared prefix of the word we are reading and the
                          previous word. When gets is successful it returns c+n
                          else it will return NULL. When the loop condition is
                          NULL the loop exits. */

        puts(c);}      /* print the unpacked word. */
Dexter CD
fonte
5

brainfuck , 201 bytes

,[[[-<+>>>+<<]>-[---<+>]<[[-<]>>]<[-]>>[<<,>>>[-[-<++++++++++>]]++++<[->+<]-[----->-<]<]<]>>>[[>>]+[-<<]>>[[>>]+[<<]>>-]]+[>>]<[-]<[<<]>[->[>>]<+<[<<]>]>[>.>]+[>[-]<,.[->+>+<<]>>----------]<[<<]>-<<<,]

Experimente online!

Requer uma nova linha à direita no final da entrada. Uma versão sem esse requisito tem 6 bytes a mais:

brainfuck , 207 bytes

,[[[-<+>>>+<<]>-[---<+>]<[[-<]>>]<[-]>>[<<,>>>[-[-<++++++++++>]]++++<[->+<]-[----->-<]<]<]>>>[[>>]+[-<<]>>[[>>]+[<<]>>-]]+[>>]<[-]<[<<]>[->[>>]<+<[<<]>]>[>.>]+[>[-]<,[->+>+<<]>>[----------<.<]>>]<[<<]>-<<<,]

Experimente online!

Ambas as versões assumem que todos os números são estritamente menores que 255.

Explicação

A fita é apresentada da seguinte forma:

tempinputcopy 85 0 inputcopy number 1 a 1 a 1 r 1 d 0 w 0 o 0 l 0 f 0 ...

A célula "número" é igual a 0 se nenhum dígito for inserido e n + 1 se o número n for inserido. A entrada é obtida na célula marcada com "85".

,[                     take input and start main loop
 [                     start number input loop
  [-<+>>>+<<]          copy input to tempinputcopy and inputcopy
  >-[---<+>]           put the number 85 in the cell where input was taken
  <[[-<]>>]            test whether input is less than 85; ending position depends on result of comparison
                       (note that digits are 48 through 57 while letters are 97 through 122)
  <[-]>                clean up by zeroing out the cell that didn't already become zero
  >[                   if input was a digit:
   <<,>>               get next input character
   >[-[-<++++++++++>]] multiply current value by 10 and add to current input
   ++++                set number cell to 4 (as part of subtracting 47)
   <[->+<]             add input plus 10*number back to number cell
   -[----->-<]         subtract 51
  <]                   move to cell we would be at if input were a letter
 <]                    move to input cell; this is occupied iff input was a digit

                       part 2: update/output word

 >>>                   move to number cell
 [                     if occupied (number was input):
  [>>]+[-<<]>>         remove existing marker 1s and decrement number cell to true value
  [[>>]+[<<]>>-]       create the correct amount of marker 1s
 ]
 +[>>]<[-]             zero out cell containing next letter from previous word
 <[<<]>                return to inputcopy
 [->[>>]<+<[<<]>]      move input copy to next letter cell
 >[>.>]                output word so far
 +[                    do until newline is read:
  >[-]<                zero out letter cell
  ,.                   input and output next letter or newline
  [->+>+<<]            copy to letter cell and following cell
  >>----------         subtract 10 to compare to newline
 ]
 <[<<]>-               zero out number cell (which was 1 to make copy loop shorter)
 <<<,                  return to input cell and take input
]                      repeat until end of input
Nitrodon
fonte
4

Python 3.6+, 172 195 156 123 122 121 104 bytes

import re
def f(l,n=0,w=""):
 for s in l:t=re.match("\d*",s)[0];n=int(t or n);w=w[:n]+s[len(t):];yield w

Experimente online!

Explicação

Eu cedi e usei expressões regulares. Isso salvou pelo menos 17 bytes. :

t=re.match("\d*",s)[0]

Quando a string não começa com um dígito, o comprimento dessa string será 0. Isso significa que:

n=int(t or n)

será nse testiver vazio e int(t)caso contrário.

w=w[:n]+s[len(t):]

remove o número do qual a expressão regular encontrada s(se não houver um número encontrado, ele remove 0caracteres, deixando sem struncamento) e substitui todos, exceto os primeiros ncaracteres da palavra anterior, pelo fragmento de palavra atual; e:

yield w

gera a palavra atual.

wizzwizz4
fonte
4

Haskell, 82 81 bytes

tail.map concat.scanl p["",""]
p[n,l]a|[(i,r)]<-reads a=[take i$n++l,r]|1<2=[n,a]

Pega e retorna uma lista de strings.

Experimente online!

        scanl p["",""]        -- fold function 'p' into the input list starting with
                              -- a list of two empty strings and collect the
                              -- intermediate results in a list
  p [n,l] a                   -- 1st string of the list 'n' is the part taken form the last word
                              -- 2nd string of the list 'l' is the part from the current line
                              -- 'a' is the code from the next line
     |[(i,r)]<-reads a        -- if 'a' can be parsed as an integer 'i' and a string 'r'
       =[take i$n++l,r]       -- go on with the first 'i' chars from the last line (-> 'n' and 'l' concatenated) and the new ending 'r'
     |1<2                     -- if parsing is not possible
       =[n,a]                 -- go on with the previous beginning of the word 'n' and the new end 'a'
                              -- e.g. [         "aa",     "2h",      "3ed",       "ing"       ] 
                              -- ->   [["",""],["","aa"],["aa","h"],["aah","ed"],["aah","ing"]]
  map concat                  -- concatenate each sublist
tail                          -- drop first element. 'scanl' saves the initial value in the list of intermediate results. 

Edit: -1 byte graças a @Nitrodon.

nimi
fonte
1
Ao contrário da sabedoria usual do golfe Haskell, você pode salvar um byte aqui, não definindo a função auxiliar como um operador de infixo.
Nitrodon
@ Nitrodon: bem localizado! Obrigado!
nimi
3

Japt, 19 18 17 bytes

Inspirado inicialmente pela solução JS da Arnauld .

;£=¯V=XkB ªV +XoB

Tente

                      :Implicit input of string array U
 £                    :Map each X
   ¯                  :  Slice U to index
      Xk              :    Remove from X
;       B             :     The lowercase alphabet (leaving only the digits or an empty string, which is falsey)
          ªV          :    Logical OR with V (initially 0)
    V=                :    Assign the result to V for the next iteration
             +        :  Append
              Xo      :  Remove everything from X, except
;               B     :   The lowercase alphabet
  =                   :  Reassign the resulting string to U for the next iteration
Shaggy
fonte
2

Gelatina , 16 bytes

⁹fØDVo©®⁸ḣ;ḟØDµ\

Experimente online!

Como funciona

⁹fØDVo©®⁸ḣ;ḟØDµ\  Main link. Argument: A (array of strings)

              µ\  Cumulatively reduce A by the link to the left.
⁹                     Yield the right argument.
  ØD                  Yield "0123456789".
 f                    Filter; keep only digits.
    V                 Eval the result. An empty string yields 0.
     o©               Perform logical OR and copy the result to the register.
       ®              Yield the value in the register (initially 0).
        ⁸ḣ            Head; keep that many character of the left argument.
          ;           Concatenate the result and the right argument.
            ØD        Yield "0123456789".
           ḟ          Filterfalse; keep only non-digits.
Dennis
fonte
1

Python 2 , 118 bytes

import re
n=0
l=input()
o=l.pop(0)
print o
for i in l:(N,x),=re.findall('(\d*)(.+)',i);n=int(N or n);o=o[:n]+x;print o

Experimente online!

Erik, o Outgolfer
fonte
1

Retina 0.8.2 , 69 bytes

+`((\d+).*¶)(\D)
$1$2$3
\d+
$*
+m`^((.)*(.).*¶(?<-2>.)*)(?(2)$)1
$1$3

Experimente online! O link inclui casos de teste mais difíceis. Explicação:

+`((\d+).*¶)(\D)
$1$2$3

Para todas as linhas que começam com letras, copie o número da linha anterior, fazendo um loop até que todas as linhas comecem com um número.

\d+
$*

Converta o número para unário.

+m`^((.)*(.).*¶(?<-2>.)*)(?(2)$)1
$1$3

Use grupos de balanceamento para substituir todos os 1s pela letra correspondente da linha anterior. (Isso acaba sendo um pouco mais desafiador do que substituir todas as séries de 1s.)

Neil
fonte
1

Vermelho , 143 bytes

func[b][a: charset[#"a"-#"z"]u: b/1 n: 0 foreach c b[parse c[copy m to a
p: copy s to end(if p<> c[n: do m]print u: rejoin[copy/part u n s])]]]

Experimente online!

Galen Ivanov
fonte
1

Java (JDK) , 150 bytes

a->{String p="",s[];for(int n=0,i=0;i<a.length;a[i]=p=p.substring(0,n=s.length<1?n:new Short(s[0]))+a[i++].replaceAll("\\d",""))s=a[i].split("\\D+");}

Experimente online!

Olivier Grégoire
fonte
1

Groovy , 74 bytes

{w="";d=0;it.replaceAll(/(\d*)(.+)/){d=(it[1]?:d)as int;w=w[0..<d]+it[2]}}

Experimente online!

Explicação:

{                                                                        }  Closure, sole argument = it
 w="";d=0;                                                                  Initialize variables
          it.replaceAll(/(\d*)(.+)/){                                   }   Replace every line (since this matches every line) and implicitly return. Loop variable is again it
                                     d=(it[1]?:d)as int;                    If a number is matched, set d to the number as an integer, else keep the value
                                                        w=w[0..<d]+it[2]    Set w to the first d characters of w, plus the matched string
Somente ASCII
fonte
0

Gelatina , 27 bytes

f€ȯ@\V,ɗḟ€ɗØDZẎḊṖḣ2/Ż;"f€Øa

Experimente online!

Erik, o Outgolfer
fonte
0

Perl 5 -p , 45 41 bytes

s:\d*:substr($p,0,$l=$&+$l*/^\D/):e;$p=$_

Experimente online!

Explicação:

s:\d*:substr($p,0,$l=$&+$l*/^\D/):e;$p=$_ Full program, implicit input
s:   :                           :e;      Replace
  \d*                                       Any number of digits
      substr($p,0,              )           By a prefix of $p (previous result or "")
                  $l=  +                      With a length (assigned to $l) of the sum
                     $&                         of the matched digits
                          *                     and the product
                        $l                        of $l (previous length or 0)
                           /^\D/                  and whether there is no number in the beginning (1 or 0)
                                                (product is $l if no number)
                                    $p=$_ Assign output to $p
                                          Implicit output
wastl
fonte
0

Groovy , 103 99 bytes

{w=it[0];d=0;it.collect{m=it=~/(\d+)(.+)/;i=m.find()?{d=m[0][1] as int;m[0][2]}():it;w=w[0..<d]+i}}

Experimente online!

GolfIsAGoodWalkSpoilt
fonte
76?
somente ASCII
1
74?
somente ASCII
0

05AB1E , 20 19 17 bytes

õUvyþDõÊi£U}Xyá«=

Experimente online ou verifique todos os casos de teste .

Explicação:

õ                  # Push an empty string ""
 U                 # Pop and store it in variable `X`
v                  # Loop `y` over the (implicit) input-list
 yþ                #  Push `y`, and leave only the digits (let's call it `n`)
   DõÊi  }         #  If it's NOT equal to an empty string "":
       £           #   Pop and push the first `n` characters of the string
        U          #   Pop and store it in variable `X`
          X        #  Push variable `X`
           yá      #  Push `y`, and leave only the letters
             «     #  Merge them together
              =    #  Print it (without popping)
Kevin Cruijssen
fonte
0

Lisp comum, 181 bytes

(do(w(p 0))((not(setf g(read-line t()))))(multiple-value-bind(a b)(parse-integer g :junk-allowed t)(setf p(or a p)w(concatenate'string(subseq w 0 p)(subseq g b)))(format t"~a~%"w)))

Experimente online!

Ungolfed:

(do (w (p 0))   ; w previous word, p previous integer prefix (initialized to 0)
    ((not (setf g (read-line t ()))))   ; read a line into new variable g
                                        ; and if null terminate: 
  (multiple-value-bind (a b)            ; let a, b the current integer prefix
      (parse-integer g :junk-allowed t) ; and the position after the prefix
    (setf p (or a p)                    ; set p to a (if nil (no numeric prefix) to 0)
          w (concatenate 'string        ; set w to the concatenation of prefix
             (subseq w 0 p)             ; characters from the previous word 
             (subseq g b)))             ; and the rest of the current line
    (format t"~a~%"w)))                 ; print the current word

Como de costume, os longos identificadores do Common Lisp o tornam não particularmente adequado para o PPCG.

Renzo
fonte
0

Python 2 , 101 100 99 bytes

import re
s=n='0'
for t in input():(m,w),=re.findall('(\d*)(.+)',t);n=m or n;s=s[:int(n)]+w;print s

Experimente online!

Chas Brown
fonte
0

C # (compilador interativo do Visual C #) , 134 bytes

a=>{int l=0,m,n;var p="";return a.Select(s=>{for(m=n=0;s[m]<58;n=n*10+s[m++]-48);return p=p.Substring(0,l=m>0?n:l)+s.Substring(m);});}

Experimente online!

-9 bytes graças a @ASCIIOnly!

Menos golfe ...

// a is an input list of strings
a=>{
  // l: last prefix length
  // m: current number of digits
  // n: current prefix length
  int l=0,m,n;
  // previous word
  var p="";
  // run a LINQ select against the input
  // s is the current word
  return a.Select(s=>{
    // nibble digits from start of the
    // current word to build up the
    // current prefix length
    for(m=n=0;
      s[m]<58;
      n=n*10+s[m++]-48);
    // append the prefix from the
    // previous word to the current
    // word and capture values
    // for the next iteration
    return
      p=p.Substring(0,l=m>0?n:l)+
      s.Substring(m);
  });
}
dana
fonte
134?
somente ASCII
Isso é muito legal :) Eu mudei l=n>0?n:lpara l=m>0?n:lporque não estava pegando o caso quando uma linha começou com zero ( 0jkl). Obrigado pela dica!
Dana 30/12
0

Scala , 226 129 102 bytes

Obrigado @ ASCII-only por seu trabalho aqui (e pela resposta do Groovy).

s=>{var(w,d,r)=("",0,"(\\d*)(.+)".r)
s map(_ match{case r(a,b)=>{if(a>"")d=a.toInt
w=w.take(d)+b}
w})}

Experimente online!

V. Courtois
fonte
: | os dois links são os mesmos
somente ASCII
sim, editando. Eu não sabia como despejá-lo e estava com pressa, então não modifiquei o que fiz.
V. Courtois
130
somente ASCII
129
somente ASCII
127
somente ASCII