単語リストから最短のパングラムを見つける


10

パングラムは、すべての文字を含む文字列ですa- z英語のアルファベット、大文字と小文字を区別しませんの。(パングラムに文字のコピーが複数含まれていたり、文字に加えて文字以外の文字が含まれていても問題ありません。)

入力が文字列のリストであり、次のプロパティを持つ1つ以上の文字列を出力するプログラムまたは関数を記述します。

  • 各出力文字列はパングラムでなければなりません。
  • 各出力文字列は、スペースで区切られた、入力リストからの1つ以上の文字列を連結して形成する必要があります。
  • 各出力文字列は、これらのプロパティを持つすべての文字列の中で、最短であるか、最短で結合されている必要があります。

多くのプログラムは、1つの文字列のみを出力することを選択します。出力を制限するために追加のコードを作成する必要がある場合にのみ、複数の文字列を出力する必要があります。

入力に印刷できない文字やスペースが含まれておらず、その中の単語が(リストの長さの自然対数の26倍)文字を超えていないと想定する場合があります。(ただし、入力に文字のみ、または小文字のみが含まれているとは限りません。句読点と大文字は完全に可能です。)

入力と出力は、適切な形式で指定できます。プログラムのテストには、2つのテストケースを使用することをお勧めします。英語の単語の辞書(ほとんどのコンピューターには1つあります)と、次のケース(完全な(26文字)のパングラムが不可能な場合)を見つける必要があります。重複した文字を含む):

abcdefghi
defghijkl
ijklmnop
lmnopqrs
opqrstuvw
rstuvwxyz

提出物とともに、プログラムの出力のサンプルを含める必要があります。(これは、異なる単語リストを使用した結果、人によって異なる場合があります。)

勝利条件

これは、課題です。勝者は、多項式時間で実行される最も短いプログラム(バイト単位)です。(それが何を意味するのかを知らない人のための要約:単語リストのサイズを2倍にすると、プログラムは一定の係数以下で遅くなるはずです。しかし、問題の定数係数はあなたと同じくらい大きくすることができますたとえば、4倍または8倍遅くなることは有効ですが、単語リストの長さの係数だけ小さくなることはありません。遅くなる係数には制限があるはずです。)


複雑さを決定するときに、各単語の長さが最大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そのような文字列が存在しない場合に返されます。)


面白い。それがどのように機能するかについてさらに詳しく説明してもらえますか?
DepressedDaniel

1
これは有効な/競合するエントリである必要があります。これは実際にはO(n log n)で実行されると思います!(マップには2²⁶エントリのハードリミットがあるため、複雑さでは表示されません。したがって、費やされる時間は入力の読み取り時間のみです。)

私は説明を読み直すだけで、それがどのように機能するかがわかります。きちんと。+1 ...うーん、ペアを考慮してマップを拡張しようとするのをやめようとするのはいつですか リラックスすることができなくなるまで、それは続くべきです。
DepressedDaniel 2016

@DepressedDaniel元の単語リストから抽出されたビットマスクごとに、これまでに見つかったすべての部分パングラムをチェックし、単語を追加すると、組み合わせたビットマスクで現在わかっているパングラムより短いパングラムが作成されるかどうかを確認します。
Neil

@ ais523大きな入力(> 1000ワード)の場合、ほとんどの時間はスワッピングに費やされているようです。マップからアレイに切り替えてみましたが、さらに遅くなりました!
Neil

-1

Pythonの3、9894、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 16

1
このソリューションの複雑さは何ですか?答えが多項式の複雑さであることが必要です。それ以外の場合は、競合しません。
NoOneIsHere 16

入力リストの長さが異なりますができますので、申し訳ありませんが、私は、それはO(n)のだと思う
エーリヒ・

入力リストの長さはさまざまですが、2番目のループは常に65から90になるため、O(n)だと思います。しかし、テストしていません。
Erich

これが「各出力文字列は、これらのプロパティを持つすべての文字列の中で最短であるか、最短である必要があります」を満たしているかどうかはわかりません。
DepressedDaniel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.