가장 짧은 공통 슈퍼 스트링


26

주어진 문자열 목록은 각각을 하위 문자열 로 포함 s_0, s_1, ..., s_n하는 가장 짧은 문자열 S찾습니다 .s_0, s_1, ..., s_n

:

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

이 문제를 해결하는 가장 짧은 프로그램 (또는 기능)을 작성하십시오. 원하는 경우 문자열을 배열 또는 문자 / 정수 목록으로 표시 할 수 있습니다. 표준 라이브러리는 괜찮습니다. 입 / 출력의 경우 STDIN / STDOUT, 사용자 프롬프트, 함수의 매개 변수 / 반환 값 등보다 편리한 것을 사용할 수 있습니다.

성능은 중요하지 않습니다. 총 길이가 100 자 미만인 문자의 경우 평균 최신 하드웨어에서 10 초 이내에 결과를 계산해야합니다.


3
+1 좋은 질문입니다. 사람들이 제출물이 다양한 사례를 처리 할 수 ​​있는지 여부를 쉽게 판단 할 수 있도록 예상 결과의 몇 가지 추가 예를 포함하는 것이 좋습니다.
DavidC

입력 / 출력은 어떻게 처리해야합니까? 결과가 함수에서 인쇄되거나 반환되어야합니까?
flornquake

따라서 "모든 문자열에 대해 ...가 모두 포함되어 있으면 반환하십시오"는 올바른 해결책이 아닙니다.
John Dvorak

답이 있을지 의심됩니다. 이 질문은 (코드-골프 부분이없는) 스택 오버플로에 매우 적합 할 수 있습니다.
John Dvorak

답변:


8

파이썬 2, 170 153/157/159

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)

두 번째 줄 바꿈은 필요하지 않습니다.

입력 : 'LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE'
출력 :SEDOLOREMAGNAD

긴 입력 문자열을 사용하더라도 최대 7 개의 입력 문자열이있는 경우 2 초 미만으로 실행됩니다 (예에서와 같이 내 컴퓨터 에서 1.7 1.5 초로 실행 됨 ). 그러나 8 개 이상의 입력 문자열을 사용하면 시간이 복잡하기 때문에 10 초 이상 걸립니다 O(n!).

Baptiste가 지적했듯이 임의의 입력 길이를 지원 해야하는 경우 (코드의 총 길이는 157 자로) range(99)대체 range(len(w))해야합니다. 빈 입력 문자열을 지원해야하는 경우로 변경해야 range(len(w)+1)합니다. 그래도 range(99)총 입력 길이가 200 미만인 경우 올바르게 작동 한다고 생각 합니다.

더 많은 테스트 :

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

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

5

Mathematica 337 418 372

Mathematica를 사용하여 구현에 실패한 후 LongestCommonSubsequencePositions패턴 일치로 전환했습니다.

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

패턴 일치 규칙

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

반환 (문자의 나열로 표현) 단어의 순서 쌍을한다 : (1) 단어, {a,y}그리고 {y,b}(2) 일반 문자열 다음에, y그 링크, 다른 단어의 시작과 함께 한 단어의 끝과, 마지막으로, {a,y,b}입력 단어를 대체 할 결합 된 단어 . 관련 예제는 Belisarius를 참조하십시오 : https://mathematica.stackexchange.com/questions/6144/looking-for-longest-common-substring-solution

3 개의 연속 된 밑줄 문자는 요소가 0 개 이상의 문자 시퀀스임을 나타냅니다.

Reverse나중에 두 가지 주문을 테스트하기 위해 사용됩니다. 연결 가능한 문자를 공유하는 쌍은 변경되지 않고 반환됩니다.

편집 :

다음은 @flornquake의 의견에 대한 응답으로 다른 단어에서 "매장 된"(즉, 완전히 포함 된) 단어를 목록에서 제거합니다.

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

:

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

보고

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


용법

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

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

"로옴"

{0.006256, "SEDOLOREMAGNAD"}


이 입력에 작동합니까 "LOREM", "ORE", "R"?
flornquake

(즉, 올바른 출력을 생성 "LOREM"합니까?)
flornquake

@flornquake. 잘 잡았다. 현재 버전에서 해결했습니다. 다른 사례를 놓치지 않았기를 바랍니다. 감사.
DavidC

별거 아니지만 최고야!
DavidC

3

GolfScript, 66 자

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

상당히 짧지 만 지수 시간 복잡성 (및 GolfScript)이 실제로 느리기 때문에 10 초 제한 시간을 초과합니다.

예 :

['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

파이썬 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)

입력 : ['LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE']
출력 :SEDOLOREMAGNAD

편집하다

reduce더러운 수입품을 사용 하면이를 더 줄일 수 있습니다 (한 줄로만 가능)!

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)

편집 2

flornquake가 지적했듯이, 이것은 한 단어가 다른 단어에 포함될 때 잘못된 결과를 제공합니다. 이것에 대한 수정은 또 다른 13 문자를 추가합니다 :

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)

정리 버전은 다음과 같습니다.

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

range(99)대신에 range(len(x))(이것을 생각하기 위해 flornquake에 대한 신용)를 사용하여 이론적 인 정확성 을 희생시키면서 몇 글자 를 깎을 수 있습니다.


정확성을 기꺼이 희생하려면 탐욕스러운 접근 방법 또는 다항식 근사 계수 2 접근 방법을 사용할 수도 있습니다.
피터 테일러

좋은 해결책! 새 문자열이 이미 슈퍼 스트링에 있는지 확인해야합니다 . 'LOREM', 'ORE', 'R'출력이 잘못 생성 LOREMORER됩니다.
flornquake

@flornquake 잘 잡았습니다. 문제를 해결했지만 13자를 추가했습니다.
Baptiste M.

1

파이썬, 144 자

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]

SA여전히 배치해야 할 단어 세트 와 s지금까지 배치 된 단어를 포함 하는 문자열 을 사용합니다. 우리는 나머지 단어 선택 a에서를 A하고에서 중복 0으로 len(a)의 끝 문자 s.

주어진 예에서 약 0.15 초만 걸립니다.


정말 좋습니다! 그러나 다른 솔루션과 마찬가지로 입력과 같이 작동하지 않습니다 ['LOREM', 'ORE', 'R']. 나는 그것을 고치고 귀하의 솔루션을 더 골프화하기 위해 자유를 얻었습니다 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(두 번째 줄은 필요하지 않습니다). 사용법 :를 S({'LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE'})반환합니다 'SEDOLOREMAGNAD'.
flornquake

0

하스켈, 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 ""

함수를 이름에 바인딩 할 필요가없는 경우 빼기 2

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.