Siêu dây phổ biến ngắn nhất


26

Đưa ra một danh sách các chuỗi s_0, s_1, ..., s_ntìm chuỗi ngắn nhất Schứa mỗi chuỗis_0, s_1, ..., s_n dưới dạng một chuỗi con .

Ví dụ :

  • S('LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE')='SEDOLOREMAGNAD'
  • S('ABCDE', 'BCD', 'C')='ABCDE'

Viết chương trình (hoặc hàm) ngắn nhất giải quyết vấn đề này. Bạn có thể biểu diễn các chuỗi dưới dạng mảng hoặc danh sách các ký tự / số nguyên nếu bạn muốn. Thư viện tiêu chuẩn là OK. Đối với đầu vào / đầu ra, bạn có thể sử dụng bất cứ điều gì thuận tiện hơn: STDIN / STDOUT, dấu nhắc người dùng, giá trị tham số / trả về của hàm, v.v.

Hiệu suất không quan trọng - giả sử, đối với đầu vào có tổng độ dài <100 ký tự, kết quả phải được tính trong <10 giây trên phần cứng hiện đại trung bình.


3
+1 Câu hỏi hay. Tôi đề nghị bạn bao gồm một số ví dụ bổ sung về kết quả mong đợi để mọi người có thể dễ dàng đánh giá xem các bài nộp có thể xử lý nhiều trường hợp khác nhau hay không.
DavidC

Nên xử lý đầu vào / đầu ra như thế nào? Kết quả nên được in hoặc trả về từ một chức năng?
flornquake

vì vậy, không "cho mọi chuỗi, nếu nó chứa tất cả ..., trả lại nó" không phải là một giải pháp hợp lệ?
John Dvorak

Tôi nghi ngờ sẽ có một câu trả lời. Câu hỏi này có thể phù hợp với Stack Overflow (không có phần golf-code) khá tốt.
John Dvorak

Câu trả lời:


8

Con trăn 2, 170 153/157/159

Rút ngắn nhờ một số ý tưởng của Baptiste .

from itertools import*
print min((reduce(lambda s,w:(w+s[max(i*(s[:i]==w[-i:])for i in range(99)):],s)[w in s],p)
for p in permutations(input())),key=len)

Việc ngắt dòng thứ hai là không cần thiết.

Đầu vào: 'LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE'
Đầu ra:SEDOLOREMAGNAD

Ngay cả với các chuỗi đầu vào dài, chuỗi này chỉ chạy trong chưa đầy 2 giây nếu có nhiều nhất 7 chuỗi đầu vào (như trường hợp trong ví dụ đã cho, chạy trong 1,7 1,5 giây trên máy của tôi). Tuy nhiên, với 8 chuỗi đầu vào trở lên, phải mất hơn 10 giây, vì độ phức tạp của thời gian là O(n!).

Như Baptiste đã chỉ ra, range(99)cần phải được thay thế bằng range(len(w))nếu độ dài đầu vào tùy ý cần được hỗ trợ (làm cho tổng chiều dài của mã 157 ký tự). Nếu các chuỗi đầu vào trống cần được hỗ trợ, nó phải được thay đổi thành range(len(w)+1). Tôi nghĩ rằng range(99)hoạt động chính xác cho bất kỳ tổng chiều dài đầu vào ít hơn 200, mặc dù.

Các xét nghiệm khác:

>>> "AD", "DO", "DOLOR", "DOLORE", "LOREM", "MAGNA", "SED", "ORE",  "R"
SEDOLOREMAGNAD

>>> 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvw
... xyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu
... vwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ZOOM', 'aZ', 'Za', 'ZA'
aZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZOOM

5

Toán học 337 418 372

Sau khi thử không thành công để thực hiện bằng Mathicala LongestCommonSubsequencePositions, tôi chuyển sang khớp mẫu.

v=Length;
p[t_]:=Subsets[t,{2}];
f[w_]:=Module[{c,x,s=Flatten,r={{a___,Longest[y__]},{y__,b___}}:>{{a,y},{y,b},{y},{a,y,b}}},
c=p@w;
x=SortBy[Cases[s[{#/.r,(Reverse@#)/.r}&/@c,1],{_,_,_,_}],v[#[[3]]]&][[-1]];
Append[Complement[w,{x[[1]],x[[2]]}],x[[4]]]]

g[r_]:=With[{h=Complement[r,Cases[Join[p@r,p@Reverse@r],y_/;!StringFreeQ@@y:>y[[2]]]]},
FixedPoint[f,Characters/@h,v@h-1]<>""]

Quy tắc khớp mẫu,

r={{a___,Longest[y__]},{y__,b___}}:> {{a,y},{y,b},{y},{a,y,b}}},

lấy một cặp từ được sắp xếp (được biểu thị dưới dạng danh sách các ký tự) và trả về: (1) các từ {a,y}{y,b}theo sau là (2) chuỗi con chung y, liên kết phần cuối của một từ với đầu của từ khác, và, cuối cùng, từ kết hợp {a,y,b}sẽ thay thế các từ đầu vào. Xem Belisarius để biết ví dụ liên quan: https://mathIALa.stackexchange.com/questions/6144/looking-for-longest-common-subopes-solution

Ba ký tự gạch dưới liên tiếp biểu thị rằng phần tử là một chuỗi gồm 0 hoặc nhiều ký tự.

Reverseđược sử dụng sau này để đảm bảo rằng cả hai đơn hàng đều được kiểm tra. Những cặp chia sẻ chữ cái liên kết được trả lại không thay đổi và bỏ qua.

Chỉnh sửa :

Các từ sau sẽ xóa khỏi danh sách các từ "bị chôn vùi" (nghĩa là chứa đầy đủ) trong một từ khác, (để đáp lại nhận xét của @ flornquake).

h=Complement[r,Cases[Join[p@r,p@Reverse@r],x_/;!StringFreeQ@@x:> x[[2]]]]

Ví dụ :

 {{"D", "O", "L", "O", "R", "E"}, {"L", "O", "R", "E", "M"}} /. r

trả lại

{{"D", "O", "L", "O", "R", "E"}, {"L", "O", "R", "E", "M"}, { "L", "O", "R", "E"}, {"D", "O", "L", "O", "R", "E", "M"}}


Sử dụng

g[{"LOREM", "ORE", "R"}]

AbsoluteTiming[g[{"AD", "DO", "DOLOR", "DOLORE", "LOREM", "MAGNA", "SED", "ORE",  "R"}]]

"LOREM"

{0,006256, "SEDOLOREMAGNAD"}


Điều này làm việc cho đầu vào "LOREM", "ORE", "R"?
flornquake

(Tức là, nó có tạo ra đầu ra chính xác "LOREM"không?)
flornquake

@flornquake. Bắt đẹp. Tôi đã giải quyết nó trong phiên bản hiện tại. Tôi hy vọng tôi đã không bỏ lỡ bất kỳ trường hợp khác. Cảm ơn.
DavidC

Không gì ngoài tốt nhất!
DavidC

3

GolfScript, 66 ký tự

{.,1>{.`{[1$]-s:h;.,),\`{:g<`{\+.g?0<{;}*}+h%~}+/}+%.&}*}:s~{,}$0=

Khá ngắn, nhưng do độ phức tạp thời gian theo cấp số nhân (và GolfScript) thực sự chậm, nó phá vỡ giới hạn thời gian 10 giây.

Ví dụ:

['LOREM' 'DOLOR' 'SED' 'DO' 'MAGNA' 'AD' 'DOLORE']
{.,1>{.`{[1$]-s:h;.,),\`{:g<`{\+.g?0<{;}*}+h%~}+/}+%.&}*}:s~{,}$0=
# => SEDOLOREMAGNAD

['AB' 'BC' 'CA' 'BCD' 'CDE']
{.,1>{.`{[1$]-s:h;.,),\`{:g<`{\+.g?0<{;}*}+h%~}+/}+%.&}*}:s~{,}$0=
# => CABCDE

2

Con trăn 2, 203 187 200

from itertools import permutations as p
def n(c,s=''):
 for x in c:s+=x[next((i+1 for i,l in [(j,x[:j+1])for j in range(len(x))][::-1]if s.endswith(l)),0):]
 return s
print min(map(n,p(input())),key=len)

Đầu vào: ['LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE']
Đầu ra:SEDOLOREMAGNAD

Chỉnh sửa

Sử dụng reducevà một số mánh khóe nhập khẩu bẩn, tôi có thể giảm thêm điều này (và chỉ một dòng!):

print min((reduce(lambda a,x:a+x[next((i+1 for i,l in [(j,x[:j+1])for j in range(len(x))][::-1]if a.endswith(l)),0):],P,'')for P in __import__('itertools').permutations(input())),key=len)

Chỉnh sửa 2

Như flornquake đã lưu ý, điều này cho kết quả không chính xác khi một từ được chứa trong một từ khác. Bản sửa lỗi này thêm 13 ký tự khác:

print min((reduce(lambda a,x:a+(x[next((i+1 for i,l in [(j,x[:j+1])for j in range(len(x))][::-1]if a.endswith(l)),0):],'')[x in a],P,'')for P in __import__('itertools').permutations(input())),key=len)

Đây là phiên bản đã dọn sạch:

from itertools import permutations

def solve(*strings):
    """
    Given a list of strings, return the shortest string that contains them all.
    """
    return min((simplify(p) for p in permutations(strings)), key=len)

def prefixes(s):
    """
    Return a list of all the prefixes of the given string (including itself),
    in ascending order (from shortest to longest).
    """
    return [s[:i+1] for i in range(len(s))]
    return [(i,s[:i+1]) for i in range(len(s))][::-1]

def simplify(strings):
    """
    Given a list of strings, concatenate them wile removing overlaps between
    successive elements.
    """
    ret = ''
    for s in strings:
        if s in ret:
            break
        for i, prefix in reversed(list(enumerate(prefixes(s)))):
            if ret.endswith(prefix):
                ret += s[i+1:]
                break
        else:
            ret += s
    return ret

print solve('LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE')

Có thể loại bỏ một vài ký tự với chi phí chính xác về mặt lý thuyết bằng cách sử dụng range(99)thay vì range(len(x))(tín dụng cho sự phô trương khi nghĩ về điều này).


Nếu bạn sẵn sàng hy sinh tính đúng đắn thì bạn cũng có thể sử dụng phương pháp tham lam hoặc hệ số xấp xỉ đa thức của 2 phương pháp.
Peter Taylor

Giải pháp tốt đẹp! Tuy nhiên, bạn cần kiểm tra xem các từ mới đã có trong siêu chuỗi chưa: 'LOREM', 'ORE', 'R'tạo ra đầu ra không chính xác LOREMORER.
flornquake

@flornquake Bắt tốt. Tôi đã cố gắng sửa nó nhưng nó có thêm 13 ký tự.
Baptiste M.

1

Con trăn, 144 ký tự

S=lambda A,s:min(S(A-set([a]),s+a[i:])for a in A for i in range(len(a)+1)if i==0 or s[-i:]==a[:i])if A else(len(s),s)
T=lambda L:S(set(L),'')[1]

Slấy một tập hợp các từ Avẫn cần đặt và một chuỗi schứa các từ được đặt cho đến nay. Chúng tôi chọn một từ còn lại atừ Avà chồng chéo từ đó 0đến các len(a)ký tự ở cuối s.

Chỉ mất khoảng 0,15 giây trên ví dụ đã cho.


Thực sự tốt đẹp! Nhưng giống như một số giải pháp khác, điều này không hoạt động cho đầu vào như thế nào ['LOREM', 'ORE', 'R']. Tôi đã có quyền tự do để khắc phục điều đó và đánh golf giải pháp của bạn thêm một số: S=lambda A,s='':A and min((S(A-{a},(s+a[max(i*(s[-i:]==a[:i])for i in range(len(a))):],s)[a in s])for a in A),key=len)or s(không cần một dòng thứ hai). Cách sử dụng: S({'LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE'})trả về 'SEDOLOREMAGNAD'.
flornquake

0

Haskell, 121

import Data.List
a p []=[(length p,p)]
a p s=[r|w<-s,t<-tails w,isInfixOf w$p++t,r<-a(p++t)(s\\[w])]
s=snd.minimum.a ""

Trừ hai nếu hàm không cần bị ràng buộc với tên

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.