Найкоротший звичайний суперструн


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

Отже, жодне "для кожного рядка, якщо він містить усі ..., повернути його" не є правильним рішенням?
Джон Дворак

Я сумніваюся, що відповідь буде. Це питання може цілком відповідати Stack Overflow (без кодової частини гольфу).
Джон Дворак

Відповіді:


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

Навіть з довгими вхідними рядками, це працює менше ніж за 2 секунди, якщо є щонайбільше 7 вхідних рядків (як це відбувається у наведеному прикладі, який працює на моїй машині 1,7 1,5 секунди). Однак 8 або більше вхідних рядків займає більше 10 секунд, оскільки складність у часі є O(n!).

Як вказував Баптист, 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

Математика 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}яке замінить вхідні слова. Дивіться Велісарія за відповідним прикладом: /mathematica/6144/looking-for-lolong-common-substring-solution

Три послідовні символи підкреслення означають, що елемент є послідовністю нуля або більше символів.

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

"LOREM"

{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 символів.
Батист М.

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]

Sбере набір слів, Aякі все ще потребують розміщення, і рядок, 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 ""

Мінус два, якщо функцію не потрібно прив’язувати до імені

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.