Evitando rios


48

fundo

Na tipografia, os rios são lacunas visuais em um bloco de texto, que ocorrem devido ao alinhamento coincidente de espaços. Isso é particularmente irritante, pois seu cérebro parece captá-los mais facilmente na visão periférica, que constantemente distrai os olhos.

Como exemplo, considere o seguinte bloco de texto, linhas quebradas para que a largura da linha não exceda 82 caracteres :

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eismod tempor
incididunt ut labore et dolore maga aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet,
consectetur adipisicing elit, sed do eismod tempor incididunt ut labore et dolore
maga aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.

Há um rio que mede seis linhas na parte inferior direita, que destaquei no bloco a seguir:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eismod tempor
incididunt ut labore et dolore maga aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum. Lorem█ipsum dolor sit amet,
consectetur adipisicing elit, sed do eismod tempor█incididunt ut labore et dolore
maga aliqua. Ut enim ad minim veniam, quis nostrud█exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute█irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla█pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui█officia deserunt mollit anim id
est laborum.

Podemos atenuar isso escolhendo uma largura de coluna ligeiramente diferente. Por exemplo, se traçarmos o mesmo texto usando linhas com mais de 78 caracteres , não haverá rio com mais de duas linhas:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eismod tempor
incididunt ut labore et dolore maga aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor
sit amet, consectetur adipisicing elit, sed do eismod tempor incididunt ut
labore et dolore maga aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.

Observe que, para o propósito desta pergunta, estamos considerando apenas fontes monoespaçadas, de modo que os rios sejam simplesmente colunas verticais de espaços. O comprimento de um rio é o número de linhas que ele abrange.

Além disso: se você é interessante na detecção de rios em fontes proporcionais, existem algumas postagens interessantes na rede.

O desafio

Você recebe uma sequência de caracteres ASCII imprimíveis (pontos de código 0x20 a 0x7E) - ou seja, uma única linha. Imprima este texto, com uma largura de linha entre 70 e 90 caracteres (inclusive), de modo que o comprimento máximo de qualquer rio no texto seja minimizado. Se houver várias larguras de texto com o mesmo comprimento mínimo (mínimo) do rio, escolha a largura mais estreita. O exemplo acima com 78 caracteres é a saída correta para esse texto.

Para quebrar linhas, você deve substituir caracteres de espaço (0x20) por quebras de linha, para que as linhas resultantes tenham o máximo de caracteres possível, mas não mais do que a largura do texto escolhido. Observe que a quebra de linha resultante em si não faz parte dessa contagem. Como exemplo, no último bloco acima, Lorem[...]temporcontém 78 caracteres, que também é a largura do texto.

Você pode assumir que a entrada não conterá espaços consecutivos e não terá espaços à esquerda ou à direita. Você também pode assumir que nenhuma palavra (substring consecutivo de não-espaços) conterá mais de 70 caracteres.

Você pode escrever um programa ou função, inserindo STDIN, argumento da linha de comando ou argumento da função e imprimindo o resultado em STDOUT.

Isso é código de golfe, então a resposta mais curta (em bytes) vence.


Penso que nos seus exemplos de quebra automática de coluna de 78 e 82, a última e a penúltima linhas estão incorretas. No exemplo 82, a última interrupção deve estar entre id e est , e no exemplo 78, deve estar entre in e culpa . Ou estou fazendo algo errado?
precisa saber é o seguinte

@Optimizer O desempate é o comprimento do texto, não o comprimento do rio.
FryAmTheEggman

Eu acho que não conta como um rio oficial, mas no exemplo comprimento 78 caracteres no máximo, parece haver um rio muito longo diagonal no top-ish esquerda-ish área
markasoftware

Consideramos casos como este como rios contínuos?
Optimizer

Grande desafio! Hm, próximo poderia ser sobre ter (não puramente vertical) rios moldar letras subliminares;)
Tobias KIENZLER

Respostas:


7

CJam, 116 106 99 84 77 72 bytes

l:X;93,72>{:D;OOXS/{S+_2$+,D<{+}{@@);a+\}?}/a+}%{z'K*S/:!0a/1fb$W=}$0=N*

Pega a entrada de linha única e imprime a saída correta em STDOUT.

UPDATE : Aprimorou muito e removeu loops redundantes fazendo todos os cálculos no próprio loop de classificação. Também foi corrigido um erro no cálculo do comprimento do rio.

Explicação em breve (depois de eu jogar ainda mais)

Experimente aqui


@Optimizer Você pode usar a entrada do ARGV, mas pode fazê-lo em ea~vez de Xcada vez. Salva dois bytes.
9303 Martin Ender #

12

Ruby 162 160 158 152 160 160 157 ( demo )

i=gets+' '
(69..s=r=89).map{|c|w=i.scan(/(.{1,#{c}}\S) /).flatten
m=(0..c).map{|i|w.map{|l|l[i]}+[?x]}.join.scan(/ +/).map(&:size).max
m<s&&(s=m;r=w)}
puts r

A versão sem golfe:

input = gets+' '

result = ''

(69..smallest_max=89).each{|w|
  #split text into words of at most w characters
  wrap = (input+' ').scan(/(.{1,#{w}}\S) /).flatten

  #transpose lines and find biggest "river"
  max_crt_river = (0..99).map{|i| wrap.map{|l|l[i]} }.flatten.join.scan(/ +/).max_by(&:size).size

  if max_crt_river < smallest_max
    smallest_max = max_crt_river
    result = wrap.join ?\n
  end
}
puts result

O @ MartinBüttner %r{...}me permite usar a interpolação de strings. Eu apenas tentei 21.times, mas isso tem mais implicações no futuro e não consegui chegar a uma solução mais curta.
Cristian Lupascu

@ MartinBüttner Você está certo, ele faz o trabalho! Eu editei minha resposta. Obrigado!
Cristian Lupascu

Isso não funciona com pastebin.com/vN2iAzNd
Joshpbarron

@Joshpbarron Muito bem visto! Eu consertei agora.
Cristian Lupascu 7/11

8

APL (105)

{∊{1↓∊⍵,3⊃⎕TC}¨⊃G/⍨V=⌊/V←{⌈/≢¨⊂⍨¨↓⍉2≠⌿+\↑≢¨¨⍵}¨G←(K⊂⍨' '=K←' ',⍵)∘{×⍴⍺:(⊂z/⍺),⍵∇⍨⍺/⍨~z←⍵>+\≢¨⍺⋄⍺}¨70+⍳21}

Explicação:

  • (K⊂⍨' '=K←' ',⍵): Adicione um espaço na frente e depois divida nos espaços. Cada palavra mantém o espaço com o qual começa.
  • ∘{... }¨70+⍳21: com esse valor, para cada número no intervalo [71, 91]: (devido à maneira como as palavras são divididas, cada 'linha' termina com um espaço extra no início, que será removido posteriormente. O intervalo é alterado por um para compensar o espaço extra.)
    • ×⍴⍺:: se ainda houver palavras,
      • z←⍵>+\≢¨⍺: obtenha o comprimento de cada palavra e calcule um total em execução do comprimento, por palavra. Marque com 1todas as palavras que podem ser usadas para preencher a próxima linha e armazene-a z.
      • (⊂z/⍺),⍵∇⍨⍺⍨~z: pegue essas palavras e processe o que resta da lista.
    • ⋄⍺: caso contrário, retorne (que agora está vazio).
  • G←: armazena a lista de listas de linhas em G(uma para cada comprimento de linha possível).
  • V←{... }¨G: para cada possibilidade, calcule o comprimento do rio mais longo e armazene-o em V:
    • +\↑≢¨¨⍵: obtenha o comprimento de cada palavra (novamente) e faça uma matriz com os comprimentos. Calcule o total em execução para cada linha nas linhas da matriz. (Portanto, o espaço extra no início de cada linha é ignorado.)
    • 2≠⌿: para cada coluna da matriz, veja se o comprimento atual da linha nesse ponto não corresponde à linha após ela. Nesse caso, não há rio lá.
    • ⊂⍨¨↓⍉: divida cada coluna da matriz sozinha (no ( 1s)). Isso fornece uma lista de listas, onde para cada rio haverá uma lista [1, 0, 0, ...], dependendo da extensão do rio. Se não houver rio, a lista será [1].
    • ⌈/≢¨: obtenha o comprimento de cada rio e obtenha o valor máximo disso.
  • ⊃G/⍨V=⌊/V: from G, selecione o primeiro item para o qual o comprimento do rio mais longo é igual ao mínimo para todos os itens.
  • {1↓∊⍵,3⊃⎕TC}¨: para cada linha, junte todas as palavras, remova o primeiro item (o espaço extra desde o início) e adicione uma nova linha ao final.
  • : junte todas as linhas.

Isso é 200 bytes, não 105.
user11153

3
@ user11153 Não especifiquei UTF-8 como a codificação. O conjunto de caracteres APL se encaixa em uma única página de códigos (e essa página de códigos existe ), ou seja, existe uma codificação existente pela qual cada um desses caracteres se encaixa em um byte e, portanto, 105 é perfeitamente adequado.
Martin Ender

Bom saber! :)
user11153

8

Bash + coreutils, 236 157 bytes

Editado com uma abordagem diferente - um pouco mais curta do que antes:

a=(`for i in {71..91};{
for((b=1;b++<i;));{
fold -s$i<<<$@|cut -b$b|uniq -c|sort -nr|grep -m1 "[0-9]  "
}|sort -nr|sed q
}|nl -v71|sort -nk2`)
fold -s$a<<<$@

Lê a sequência de entrada da linha de comando.

Com três tipos aninhados, estremeço ao pensar qual é a complexidade do grande tempo para isso, mas ele completa o exemplo em menos de 10 segundos na minha máquina.


3

Python, 314 bytes

Muito obrigado ao SP3000, grc e FryAmTheEggman:

b=range;x=len
def p(i):
 l=[];z=''
 for n in t:
  if x(z)+x(n)<=i:z+=n+' '
  else:l+=[z];z=n+' '
 return l+[z]*(z!=l[x(l)-1])
t=input().split();q=[]
for i in b(70,91):l=p(i);q+=[max(sum(x(l[k+1])>j<x(l[k])and l[k][j]is' '==l[k+1][j]for k in b(x(l)-1))for j in b(i))]
print(*p(q.index(min(q))+70),sep='\n')

2
Mais como Pi-thon
Optimizer

3

JavaScript (ES6) 194 202

Solução iterativa, talvez mais curta se tornada recursiva

F=s=>{
  for(m=1e6,b=' ',n=70;n<91;n++)
    l=b+'x'.repeat(n),x=r=q='',
    (s+l).split(b).map(w=>
      (t=l,l+=b+w)[n]&&(
        l=w,r=r?[...t].map((c,p)=>x<(v=c>b?0:-~r[p])?x=v:v,q+=t+'\n'):[]
      )
    ),x<m&&(o=q,m=x);
  alert(o)
}

Explicado

F=s=> {
  m = 1e9; // global max river length, start at high value
  for(n=70; n < 91; n++) // loop on line length
  {
    l=' '+'x'.repeat(n), // a too long first word, to force a split and start
    x=0, // current max river length
    q='', // current line splitted text
    r=0, // current river length for each column (start 0 to mark first loop)
    (s+l) // add a too long word to force a last split. Last and first row will not be managed
    .split(' ').map(w=> // repeat for each word 
      (
        t=l, // current partial row in t (first one will be dropped)
        (l += ' '+w)[n] // add word to partial row and check if too long
        &&
        (
          l = w, // start a new partial row with current word
          r=r? // update array r if not at first loop
          ( 
            q+=t+'\n', // current row + newline added to complete text 
            [...t].map((c,p)=>( // for each char c at position p in row t
              v = c != ' ' 
                ? 0 // if c is not space, reset river length at 0
                : -~r[p], // if c is space, increment river length
              x<v ? x=v : v // if current > max, update max
            ))
          ):[]  
        )  
      )
    )
    x < m && ( // if current max less than global max, save current text and current max
      o = q,
      m = x
    )
  }
  console.log(o,m)
}

Teste no console do FireFox / FireBug.

F('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eismod tempor incididunt ut labore et dolore maga aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eismod tempor incididunt ut labore et dolore maga aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.')

Resultado

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eismod tempor
incididunt ut labore et dolore maga aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor
sit amet, consectetur adipisicing elit, sed do eismod tempor incididunt ut
labore et dolore maga aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.

3

Python 3, 329 bytes

import re,itertools as s
def b(t,n):
 l=0;o=""
 for i in t.split():
  if l+len(i)>n:o=o[:-1]+'\n';l=0
  l+=len(i)+1;o+=i+' '
 return o
t=input();o={}
for n in range(90,69,-1):o[max([len(max(re.findall('\s+',x),default='')) for x in ["".join(i) for i in s.zip_longest(*b(t,n).split('\n'),fillvalue='')]])]=n
print(b(t,o[min(o)]))

Versão não destruída:

# Iterates over words until length > n, then replaces ' ' with '\n'
def b(t,n):
    l = 0
    o = ""
    for i in t.split():
        if l + len(i) > n:
            o = o[:-1] + '\n'
            l = 0
        l += len(i) + 1
        o += i + ' '
    return o

t = input()
o = {}
# range from 90 to 70, to add to dict in right order
for n in range(90,69,-1):
    # break text at length n and split text into lines
    temp = b(t,n).split('\n')
    # convert columns into rows
    temp = itertools.zip_longest(*temp, fillvalue='')
    # convert the char tuples to strings
    temp = ["".join(i) for i in temp]
    # findall runs of spaces, get longest run and get length
    temp = [len(max(re.findall('\s+',x),default='')) for x in temp]
    # add max river length as dict key, with line length as value
    o[max(temp)] = n

print(b(t,o[min(o)]))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.