Tìm các pangram ngắn nhất từ ​​một danh sách từ


10

Một pangram là một chuỗi chứa mọi chữ cái a- zcủa bảng chữ cái tiếng Anh, không phân biệt chữ hoa chữ thường. (Không sao nếu pangram chứa nhiều bản sao của một chữ cái hoặc nếu nó chứa các ký tự không phải chữ cái ngoài các chữ cái.)

Viết chương trình hoặc hàm có đầu vào là danh sách các chuỗi và đầu ra một hoặc nhiều chuỗi có các thuộc tính sau:

  • Mỗi chuỗi đầu ra phải là một pangram.
  • Mỗi chuỗi đầu ra phải được hình thành bằng cách nối một hoặc nhiều chuỗi từ danh sách đầu vào, cách nhau bởi khoảng trắng.
  • Mỗi chuỗi đầu ra phải ngắn nhất hoặc được buộc ngắn nhất, trong số tất cả các chuỗi có các thuộc tính này.

Nhiều chương trình sẽ chọn chỉ xuất ra một chuỗi; bạn chỉ muốn xuất nhiều hơn một chuỗi nếu bạn không phải viết thêm mã để giới hạn đầu ra.

Bạn có thể giả sử rằng đầu vào không chứa các ký tự hoặc khoảng trắng không thể in được và không có từ nào trong đó nhiều hơn (26 lần logarit tự nhiên về độ dài của danh sách) các ký tự. (Tuy nhiên, bạn có thể không cho rằng đầu vào không chứa gì ngoài chữ cái hoặc chỉ chữ cái viết thường; dấu chấm câu và chữ in hoa là hoàn toàn có thể.)

Đầu vào và đầu ra có thể được đưa ra trong bất kỳ định dạng hợp lý. Để kiểm tra chương trình của bạn, tôi khuyên bạn nên sử dụng hai trường hợp kiểm tra: từ điển các từ tiếng Anh (hầu hết các máy tính đều có một) và trường hợp sau (trong đó một pangram hoàn hảo (26 chữ cái) là không thể, vì vậy bạn phải tìm một từ chứa các chữ cái trùng lặp):

abcdefghi
defghijkl
ijklmnop
lmnopqrs
opqrstuvw
rstuvwxyz

Bạn nên bao gồm một mẫu của đầu ra chương trình của bạn cùng với trình của bạn. (Điều này có thể khác nhau đối với những người khác nhau do sử dụng các danh sách từ khác nhau.)

Điều kiện chiến thắng

Đây là một thử thách . Người chiến thắng là chương trình ngắn nhất (tính bằng byte) chạy trong thời gian đa thức . (Tóm tắt cho những người không biết điều đó có nghĩa là gì: nếu bạn tăng gấp đôi kích thước của danh sách từ, chương trình sẽ trở nên chậm hơn không quá một yếu tố không đổi. Tuy nhiên, yếu tố không đổi trong câu hỏi có thể lớn như bạn như. Ví dụ, nó hợp lệ để nó trở nên chậm hơn bốn lần, hoặc chậm hơn tám lần, nhưng không trở nên nhỏ hơn bởi một yếu tố về độ dài của danh sách từ; yếu tố mà nó trở nên chậm hơn phải bị ràng buộc.)


Khi xác định độ phức tạp, chúng ta có thể sử dụng thực tế là mỗi từ dài tối đa 26 chữ cái không? Đó là kích thước bảng chữ cái là một hằng số 26?
xnor

Đúng. Tôi đặt hạn chế đó vào đầu vào ở đó một phần để làm cho sự phức tạp dễ xác định / tính toán hơn.

Tôi nghĩ rằng điều này chạy vào một kỹ thuật. Nếu bạn bỏ qua các từ đầu vào lặp đi lặp lại, có nhiều nhất là 27 ^ 26 từ đầu vào có thể, và nhiều nhất là 2 ^ (27 ^ 26) các tập hợp con có thể có của chúng như các đầu vào có thể. Điều này là rất lớn nhưng là một hằng số. Vì vậy, bất kỳ chương trình nào trên tập hữu hạn này là thời gian không đổi, với hằng số là số bước tối đa được thực hiện trên tất cả các đầu vào có thể.
xnor

Tôi đã không nói rằng không có từ trùng lặp trong đầu vào. Tôi đoán bạn có thể chạy chương trình trong thời gian "kỹ thuật" O (n) bằng cách lọc ra các dấu chấm câu và sao chép đầu vào trước, mặc dù (hoặc nhiều khả năng là O (n log n), sẽ sử dụng ít bộ nhớ hơn so với cơ số lặp lại sẽ). Sau đó, bạn phải quay lại từ phiên bản đã lọc sang danh sách từ gốc. Tuy nhiên, bạn không thể yêu cầu thời gian đa thức trong câu hỏi trừ khi bạn thực sự trải qua tất cả các bước đó!

Tôi đã quên về những chữ cái không. Chúng ta có thể cho rằng đây là ASCII, hay nói cách khác trong một số tập hữu hạn? Nếu vậy, tôi nghĩ rằng bất kỳ thuật toán nào bắt đầu bằng cách lặp lại có thể tự nhận là thời gian đa thức.
xnor

Câu trả lời:


3

Ruby 159 (lặp lại)

Hồng ngọc 227 220 229 227 221 (đệ quy)

Giải pháp lặp mới (dựa trên thuật toán được mô tả bởi @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

Giải pháp đệ quy cũ:

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

Phép đo byte dựa trên việc bỏ dòng mới cuối cùng trong tệp, điều này không quan trọng ruby 2.3.1p112. Số lượng byte đã tăng trở lại sau khi sửa một lỗi nhỏ (thêm.downcase .upcase cho trường hợp không nhạy cảm theo yêu cầu của tuyên bố vấn đề).

Đây là phiên bản cũ hơn trước khi rút ngắn số nhận dạng và như vậy:

#!/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]);

Làm thế nào nó hoạt động? Về cơ bản, nó duy trì một tập hợp các ký tự vẫn để che và chỉ đệ quy trên một từ nếu nó sẽ làm giảm tập hợp không được phát hiện. Ngoài ra, kết quả đệ quy được ghi nhớ. Mỗi tập hợp con của 2 ^ 26 tương ứng với một mục nhập bảng ghi nhớ. Mỗi mục nhập như vậy được tính theo thời gian tỷ lệ với kích thước của tệp đầu vào. Vì vậy, toàn bộ điều là O(N)( Nkích thước của tệp đầu vào), mặc dù với một hằng số rất lớn.


1

JavaScript (ES6), 249 248 byte, có thể cạnh tranh

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]

Giải thích: Chuyển đổi mảng bằng cách chuyển đổi các chữ cái thành bitmask, chỉ lưu từ ngắn nhất cho mỗi bitmask trong bản đồ. Sau đó lặp lại qua một bản sao của bản đồ, tăng bản đồ bằng cách thêm từng bitmask kết hợp nếu chuỗi kết quả sẽ ngắn hơn. Cuối cùng trả về chuỗi đã lưu cho bitmap tương ứng với một pangram. (Trả về undefinednếu không có chuỗi như vậy tồn tại.)


Hấp dẫn. Bạn có thể giải thích thêm về cách thức hoạt động của nó và, nếu có sẵn, đăng mã không mã hóa?
DepressionDaniel

1
Đây phải là một mục hợp lệ / cạnh tranh. Tôi nghĩ rằng điều này thực sự chạy trong O ( n log n ), trên thực tế! (Bản đồ có giới hạn cứng là 2²⁶ mục và do đó không hiển thị về độ phức tạp; do đó, thời gian duy nhất là thời gian đọc đầu vào.)

Tôi chỉ đọc lại mô tả và tôi hiểu làm thế nào nó hoạt động bây giờ. Khéo léo. +1 ... Hmm, khi nào nó quyết định ngừng cố gắng tăng bản đồ bằng cách xem xét các cặp? Nó nên tiếp tục đi cho đến khi không thể thư giãn.
DepressionDaniel

@DepressionDaniel Với mỗi bitmask được trích xuất từ ​​danh sách từ gốc, nó sẽ kiểm tra tất cả các pangram một phần mà nó đã tìm thấy cho đến nay, và liệu việc thêm từ đó có tạo ra một pangram ngắn hơn so với bitmask hiện tại hay không.
Neil

@ ais523 Đối với đầu vào lớn (> 1000 từ), phần lớn thời gian dường như được dành cho việc hoán đổi. Tôi đã thử chuyển từ Bản đồ sang Mảng và nó thậm chí còn chậm hơn!
Neil

-1

Python 3, 98 , 94 , 92 byte

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

Lặp lại thông qua biểu diễn ASCII của bảng chữ cái và thêm 1 vào danh sách nếu chữ cái được tìm thấy trong chuỗi. Nếu tổng của danh sách lớn hơn 25, nó chứa tất cả các chữ cái của bảng chữ cái và sẽ được in.


Tôi nghĩ rằng bạn có thể loại bỏ một khoảng cách giữa (' ')if. Bạn cũng có thể thay đổi ord(i) in range(65,91)thành 91>x>=65. Ngoài ra, những gì phức tạp?
NoOneIsHãy 8/12/2016

1
Sự phức tạp của giải pháp này là gì? Nó là cần thiết cho câu trả lời là phức tạp đa thức, nếu không nó là không cạnh tranh.
NoOneIsHãy 8/12/2016

Xin lỗi, tôi nghĩ đó là O (n), vì danh sách đầu vào có thể khác nhau về chiều dài nhưng
Erich

Xin lỗi, tôi nghĩ đó là O (n), vì danh sách đầu vào có thể khác nhau về chiều dài nhưng vòng lặp thứ hai luôn đi từ 65 đến 90. Nhưng tôi đã không kiểm tra nó.
Erich

Không chắc chắn điều này thỏa mãn "Mỗi chuỗi đầu ra phải ngắn nhất hoặc được buộc ngắn nhất, trong số tất cả các chuỗi có các thuộc tính này."
DepressionDaniel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.