Знайдіть найкоротші панграми зі списку слів


10

Панграма є рядком , яка містить всі літери a- zвід англійського алфавіту, НЕ чутливі до регістру. (Добре, якщо в панграмі міститься більше однієї копії листа або якщо крім букв вони містять небуквені символи.)

Напишіть програму або функцію, вхідною інформацією якої є список рядків і яка виводить одну або кілька рядків, які мають такі властивості:

  • Кожен вихідний рядок повинен бути панграмою.
  • Кожен вихідний рядок повинен бути сформований об'єднанням одного або декількох рядків зі списку вводу, розділених пробілами.
  • Кожен вихідний рядок повинен бути найкоротшим або прив’язаним до найкоротшого, серед усіх рядків із цими властивостями.

Багато програм вирішать вивести лише одну рядок; ви хочете вивести більше однієї струни, лише якщо вам в іншому випадку доведеться написати додатковий код, щоб обмежити вихід.

Ви можете припустити, що в тексті даних немає недрукованих символів або пробілів, і що жодне слово в ньому не перевищує (26 разів більше природного логарифму довжини списку) символів. (Ви, однак, не можете припускати, що введення не містить нічого, крім букв або лише малих літер; розділові знаки та великі літери цілком можливі.)

Введення та вихід можуть бути надані у будь-якому розумному форматі. Для тестування вашої програми я рекомендую використовувати два тестові випадки: словник англійських слів (у більшості комп'ютерів є одне) та наступний випадок (для якого ідеальна (26-літерна) панограма неможлива, тому вам доведеться знайти її що містять дублікати букв):

abcdefghi
defghijkl
ijklmnop
lmnopqrs
opqrstuvw
rstuvwxyz

Ви повинні включити зразок результатів програми разом з поданням. (Це може бути різним для різних людей внаслідок використання різних списків слів.)

Стан перемоги

Це виклик з з . Переможець - найкоротша програма (у байтах), яка працює в поліноміальний час . (Підсумок для людей, які не знають, що це означає: якщо подвоїти розмір списку слів, програма повинна стати повільнішою не більше ніж постійний коефіцієнт. Однак постійний фактор, про який йде мова, може бути таким же великим, як і ви Наприклад, допустимо, щоб воно стало в чотири рази повільніше або у вісім разів повільніше, але не для того, щоб воно стало меншим на коефіцієнт довжини списку слів; фактор, через який він стає повільніше, повинен бути обмежений.)


Визначаючи складність, чи можемо ми використати той факт, що кожне слово має довжину не більше 26 букв? Що розмір алфавіту є постійною 26?
xnor

Так. Я поставив це обмеження на вхід частково, щоб полегшити визначення / обчислення складності.

Я думаю, це стикається з технічністю. Якщо ви ігноруєте повторені слова введення, є щонайбільше 27 ^ 26 можливих вхідних слів, і так щонайбільше 2 ^ (27 ^ 26) можливих підмножин з них як можливих вводів. Це величезна, але постійна. Отже, будь-яка програма на цьому кінцевому наборі є постійним часом, при цьому постійною є максимальна кількість кроків, зроблених за всі можливі входи.
xnor

Я не сказав, що у введенні немає дублюваних слів. Я думаю, ви могли запустити програму в "технічний" O (n) час, відфільтрувавши розділові знаки та дедублірувавши вхід спочатку, хоча (або, швидше за все, O (n log n), який би використовував набагато менше пам'яті, ніж радіакс дедуплікат би). Тоді вам доведеться повернутися з відфільтрованої версії до початкового списку слів. Ви не можете вимагати поліноміального часу, про який йдеться, якщо ви фактично не пройдете всі ці кроки!

Я забув про не-букви. Чи можемо ми припустити, що це ASCII або іншим чином в межах деякого кінцевого набору? Якщо так, я думаю, що будь-який алгоритм, який починається з дедупликації, може претендувати на поліноміальний час.
xnor

Відповіді:


3

Ruby 159 (ітеративний)

Рубін 227 220 229 227 221 (рекурсивна)

Нове ітеративне рішення (засноване на алгоритмі, описаному @Niel):

c={('A'..'Z').to_a=>""}
while l=gets
d=c.clone
c.map{|k,v|j=k-l.upcase.chars
w=v+" "+l.strip
d[j]=w if !c[j]||c[j].size<w.size}
c=d
end
x=c[[]]
p x[1..-1] if x

Старе рекурсивне рішення:

W=[]
while l=gets
W<<l.strip
end
I=W.join(" ")+"!!"
C={[]=>""}
def o(r)if C[r]
C[r]
else
b=I
W.map{|x|s=r-x.upcase.chars
if s!=r
c=x+" "+o(s)
b=c if c.size<b.size
end}
C[r]=b
end
end
r=o ('A'..'Z').to_a
p r[0..-2] if r!=I

Вимірювання байтів базується на відведенні остаточного нового рядка у файлі, що не має значення ruby 2.3.1p112. Кількість байтів повернулася назад після виправлення невеликої помилки (додавання.downcase .upcase для нечутливості до випадку, як цього вимагає постановка проблеми).

Ось більш рання версія до скорочення ідентифікаторів і така:

#!/usr/bin/env ruby

$words = [];

while (line=gets)
  $words << line[0..-2];
end

$impossible = $words.join(" ")+"!!";

$cache = {};

def optimize(remaining)
  return $cache[remaining] if ($cache[remaining]);
  return "" if (remaining == []);

  best = $impossible;

  $words.each{|word|
    remaining2 = remaining - word.chars;
    if (remaining2 != remaining)
      curr = word + " " + optimize(remaining2);
      best = curr if (curr.length < best.length);
    end
  };

  $stderr.puts("optimize(#{remaining.inspect})=#{best.inspect}");

  return $cache[remaining] = best;
end

result = optimize(('a'..'z').to_a);

puts(result[0..-1]);

Як це працює? В основному він підтримує набір символів, які все ще охоплюють, і повторюється лише на слові, якщо це зменшить непокритий набір. Додатково запам'ятовуються результати рекурсії. Кожному підмножині 2 ^ 26 відповідає запис таблиці запам'ятовування. Кожен такий запис обчислюється у часі, пропорційному розміру вхідного файлу. Отже, вся справа в цьому O(N)(де Nрозмір вхідного файлу), хоча і з величезною константою.


1

JavaScript (ES6), 249 248 байт, можливо, конкуруючи

a=>a.map(w=>w.replace(/[a-z]/gi,c=>b|=1<<parseInt(c,36)-9,b=0,l=w.length)&&(m.get(b)||[])[0]<l||m.set(b,[l,w]),m=new Map)&&[...m].map(([b,[l,w]])=>m.forEach(([t,s],e)=>(m.get(e|=b)||[])[0]<=t+l||m.set(e,[t+l+1,s+' '+w])))&&(m.get(-2^-1<<27)||[])[1]

Пояснення: Перетворює масив, перетворюючи букви в бітову маску, зберігаючи лише найкоротше слово для кожної бітової маски на карті. Потім повторіть копію карти, збільште карту, додавши кожну комбіновану бітову маску, якщо отриманий рядок буде коротшим. Нарешті поверніть рядок, збережений для растрової карти, відповідну панграмі. (Повертається, undefinedякщо такої рядки не існує.)


Цікаво. Чи можете ви детальніше ознайомитись із тим, як це працює, і, якщо вони є, опублікувати код, який не використовується для гольфу?
Депресія

1
Це має бути дійсним / конкуруючим записом. Я думаю, що насправді це працює в O ( n log n ), насправді! (Карта має жорсткий ліміт в 2 ²⁶ записів, і тому вона не відображається по складності; таким чином, єдиний витрачений час - це час читання вводу.)

Я просто перечитав опис, і я зрозумів, як він працює зараз. Акуратний. +1 ... Хм, коли він вирішує припинити намагання збільшити карту, розглядаючи пари? Це повинно тривати, поки розслаблення не можливе.
ДепресіяДаніель

@DepressDaniel Для кожної біт-маски, вилученої з оригінального списку слів, вона перевіряє всі часткові панграми, які вона знайшла до цього часу, і чи додавання цього слова створює панграму, яка коротша за ту, яку вона в даний час знає для комбінованої бітової маски.
Ніл

@ ais523 Для великих входів (> 1000 слів) більшість часу, здається, витрачається на обмін. Я спробував перейти з карти на масив, і це стало ще повільніше!
Ніл

-1

Python 3, 98 , 94 , 92 байт

print([s for s in input().split()if sum([1 for c in range(65,91)if chr(c)in s.upper()])>25])

Ітератується через представлення алфавіту ASCII та додає 1 до списку, якщо буква знайдена в рядку. Якщо сума списку більше 25, він містить усі літери алфавіту і буде надрукований.


Я думаю, ви можете видалити пробіл між (' ')і if. Ви також можете змінити ord(i) in range(65,91)на 91>x>=65. Також у чому складність?
NoOneIsHere

1
У чому полягає складність цього рішення? Він необхідний для відповіді , щоб бути в полиномиальной складності, в іншому випадку вона не конкурує.
NoOneIsHere

Вибачте, я думаю, що це O (n), тому що список вхідних даних може змінюватися по довжині, але
Еріх

Вибачте, я думаю, що це O (n), оскільки список введення може змінюватись по довжині, але другий цикл завжди йде від 65 до 90. Але я не перевіряв його.
Еріх

Не впевнений, що це задовольняє "Кожен вихідний рядок повинен бути найкоротшим або прив'язаним для найкоротшого між усіма рядками з цими властивостями."
ДепресіяДаніель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.