Избегать рек


48

Фон

В типографии реки являются визуальными пробелами в блоке текста, которые возникают из-за совпадения пробелов. Это особенно раздражает, так как ваш мозг, кажется, легче воспринимает их при периферийном зрении, которое постоянно отвлекает ваши глаза.

В качестве примера возьмем следующий блок текста, строки которого разбиты так, что ширина строки не превышает 82 символов :

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.

В нижней правой части есть река, охватывающая шесть линий, которые я выделил в следующем блоке:

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.

Мы можем смягчить это, выбрав немного другую ширину столбца. Например, если мы размещаем один и тот же текст, используя строки длиной не более 78 символов , река не длиннее двух строк:

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.

Обратите внимание, что для целей этого вопроса мы рассматриваем только моноширинные шрифты, так что реки - это просто вертикальные столбцы пространств. Длина реки - это количество линий, которые она охватывает.

Если вы интересуетесь обнаружением реки пропорциональными шрифтами, в сети есть несколько интересных постов .

Соревнование

Вам дана строка печатных символов ASCII (кодовая точка от 0x20 до 0x7E) - то есть одна строка. Напечатайте этот текст с шириной строки от 70 до 90 символов (включительно), чтобы максимальная длина любой реки в тексте была минимальной. Если имеется несколько значений ширины текста с одинаковой (минимальной) максимальной длиной реки, выберите более узкую ширину. Приведенный выше пример с 78 символами является правильным выводом для этого текста.

Чтобы разбить строки, необходимо заменить пробелы (0x20) на разрывы строк, чтобы в результирующих строках было как можно больше символов, но не больше выбранной ширины текста. Обратите внимание, что результирующий разрыв строки сам по себе не является частью этого количества. Например, в последнем блоке выше Lorem[...]temporсодержится 78 символов, что также является шириной текста.

Вы можете предположить, что входные данные не будут содержать последовательные пробелы и не будут иметь начальных или конечных пробелов. Вы также можете предположить, что ни одно слово (последовательная подстрока без пробелов) не будет содержать более 70 символов.

Вы можете написать программу или функцию, принимая ввод через STDIN, аргумент командной строки или аргумент функции и печатая результат в STDOUT.

Это код гольф, поэтому самый короткий ответ (в байтах) выигрывает.


Я думаю, что в ваших примерах с 78 и 82 столбцами последняя и вторая до последней строки неверны. В примере 82 последний разрыв должен быть между id и est , а в примере 78 он должен быть между in и culpa . Или я что-то не так делаю?
Кристиан Лупаску

@Optimizer Разрыв связи - это длина текста, а не длина реки.
FryAmTheEggman

Я предполагаю, что это не считается официальной рекой, но в примере с максимальной длиной 78 символов, кажется, что есть довольно длинная диагональная река в верхней левой области
markasoftware

Как мы рассмотрим случаи , как это , как продолжается реки?
Оптимизатор

Отличный вызов! Хм, следующий может быть о том, чтобы (не чисто вертикальные) реки формировали подсознательные буквы;)
Тобиас Кинцлер

Ответы:


7

CJam, 116 106 99 84 77 72 байта

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

Принимает однострочный ввод и печатает правильный вывод в STDOUT.

ОБНОВЛЕНИЕ : Улучшено много и удалены лишние циклы, выполнив все вычисления в самом цикле сортировки. Также исправлена ​​ошибка в расчете длины реки.

Объяснение скоро (после того, как я играю в гольф это еще дальше)

Попробуй здесь


@Optimizer Вы можете использовать ввод из ARGV, тогда вы можете использовать ea~вместо Xкаждого раза. Сохраняет два байта.
Мартин Эндер

12

Рубин 162 160 158 152 160 157 ( демо )

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

Версия без гольфа:

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

@ MartinBüttner %r{...}позволяет мне использовать интерполяцию строк. Я только что попробовал 21.times, но у этого есть еще некоторые последствия в будущем, и мне не удалось найти более короткое решение.
Кристиан Лупаску,

@ MartinBüttner Вы правы, это делает работу! Я отредактировал свой ответ. Спасибо!
Кристиан Лупаску

Это не работает с pastebin.com/vN2iAzNd
Joshpbarron

@Joshpbarron Очень хорошо заметили! Я исправил это сейчас.
Кристиан Лупаску,

8

APL (105)

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

Объяснение:

  • (K⊂⍨' '=K←' ',⍵): Добавить пробел перед , а затем разделить на пробелы. Каждое слово сохраняет место, с которого оно начинается.
  • ∘{... }¨70+⍳21: с этим значением для каждого числа в диапазоне [71, 91]: (Из-за того, как слова разделяются, каждая «строка» заканчивается дополнительным пробелом в начале, который будет удален позже. Диапазон сдвигается на один, чтобы компенсировать дополнительное пространство.)
    • ×⍴⍺:: если еще остались слова,
      • z←⍵>+\≢¨⍺: получить длину для каждого слова и вычислить итоговую сумму длины для каждого слова. Отметьте 1все слова, которые можно взять, чтобы заполнить следующую строку, и сохраните это в z.
      • (⊂z/⍺),⍵∇⍨⍺⍨~z: возьми эти слова, а затем обработай то, что осталось от списка.
    • ⋄⍺: если нет, вернуть (который теперь пуст).
  • G←: сохранить список списков строк G(по одному на каждую возможную длину строки).
  • V←{... }¨G: для каждой возможности рассчитайте длину самой длинной реки и сохраните ее в V:
    • +\↑≢¨¨⍵: получить длину каждого слова (снова) и составить матрицу из длин. Вычислите промежуточные суммы для каждой строки в строках матрицы. (Таким образом, лишний пробел в начале каждой строки игнорируется.)
    • 2≠⌿: для каждого столбца матрицы посмотрите, не соответствует ли текущая длина линии в этой точке линии после нее. Если так, то там нет реки.
    • ⊂⍨¨↓⍉: разделить каждый столбец матрицы по отдельности (по 1s). Это дает список списков, где для каждой реки будет список [1, 0, 0, ...], в зависимости от длины реки. Если реки нет, список будет [1].
    • ⌈/≢¨: получить длину каждой реки и получить максимальное значение этого.
  • ⊃G/⍨V=⌊/V: from Gвыберите первый элемент, для которого длина самой длинной реки равна минимальной для всех элементов.
  • {1↓∊⍵,3⊃⎕TC}¨: для каждой строки соедините все слова вместе, удалите первый элемент (лишний пробел в начале) и добавьте новую строку в конец.
  • : соединить все линии вместе.

Это 200 байтов, а не 105.
user11153

3
@ user11153 Я не указал UTF-8 в качестве кодировки. Набор символов APL вписывается в одну кодовую страницу (и эта кодовая страница существует ), то есть существует существующая кодировка, с помощью которой каждый из этих символов помещается в байт, и, следовательно, 105 отлично подходит.
Мартин Эндер

Хорошо знать! :)
user11153

8

Bash + coreutils, 236 157 байт

Отредактировано с другим подходом - немного короче, чем раньше:

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<<<$@

Читает входную строку из командной строки.

С 3-мя вложенными сортировками я не могу понять, какова сложность времени с большим количеством O для этого, но он завершает пример менее чем за 10 секунд на моей машине.


3

Python, 314 байт

Большое спасибо SP3000, grc и 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
Больше похоже на Pi-thon
Оптимизатор

3

JavaScript (ES6) 194 202

Итеративное решение, может быть короче, если сделать рекурсивным

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)
}

Разъяснения

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)
}

Тест в консоли 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.')

Выход

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 байт

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)]))

Безголовая версия:

# 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)]))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.