最短の共通スーパーストリング


26

文字列のリストが与えられると、それぞれを部分文字列として含むs_0, s_1, ..., s_n最も短い文字列を見つけます。Ss_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

だから、「すべての文字列に...がすべて含まれている場合、それを返す」は有効な解決策ではありませんか?
ジョンドヴォルザーク

答えがあるとは思わない。この質問は、スタックオーバーフロー(コードゴルフの部分なし)に非常によく当てはまるかもしれません。
ジョンドヴォルザーク

回答:


8

Python 2、170 153/157/159

バプティストのアイデアのおかげで短縮されました。

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)

2番目の改行は必要ありません。

入力:'LOREM', 'DOLOR', 'SED', 'DO', 'MAGNA', 'AD', 'DOLORE'
出力:SEDOLOREMAGNAD

入力文字列が長い場合でも、最大7つの入力文字列がある場合、これは2秒未満で実行されます(この例では、マシンで1.7 1.5秒で実行されます)。ただし、8以上の入力文字列では、時間の複雑さはであるため、10秒以上かかりますO(n!)

Baptisteが指摘したように、任意の入力長をサポートする必要がある場合は、コードをrange(99)置き換える必要がありますrange(len(w))(コードの合計長は157文字になります)。空の入力文字列をサポートする必要がある場合は、に変更する必要があり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)共通の部分文字列、y1つの単語の終わりを他の単語の始まりとリンクする最後に、{a,y,b}入力語を置き換える結合語。関連する例については、ベリサリウスを参照してください:https : //mathematica.stackexchange.com/questions/6144/looking-for-longest-common-substring-solution

3つの連続した下線文字は、要素がゼロ個以上の文字のシーケンスであることを示します。

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

Python 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いくつかのダーティインポートトリックを使用して、これをさらに減らすことができます(そして1行だけに!)。

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文字追加されました。
バプティストM.

1

Python、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してからそれをオーバーラップ0len(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(2行目は必要ありません)。使用法:を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.