Всі можливі способи переплетення двох рядків


21

Нещодавно я бачив це запитання на stackoverflow. Це чудове питання, але є одна фатальна проблема з цим питанням. Вони просять найкращий спосіб це зробити. Наприклад, найпростіший для читання, ідіоматичний, найменший тощо. Хіба вони не знають, що не важливо? Ви повинні запитати, як це зробити з найменшими байтами коду!

Оскільки я сумніваюся, що це питання буде оцінено на стартовому потоці, я вирішив його задати тут.

Змагання

Ви повинні написати найкоротшу можливу програму або функцію, яка генерує всі можливі способи переплетення будь-яких двох довільних рядків. Наприклад, якщо два рядки є 'ab'і 'cd', вихід:

['abcd', 'acbd', 'acdb', 'cabd', 'cadb', 'cdab']

Як бачимо, aце завжди раніше b, і cзавжди є раніше d.

IO може бути в будь-якому розумному форматі. Використовуйте цей код python для перевірки результатів. (кредит: JeD )

def shuffle(s,t):
    if s=="":
        return [t]
    elif t=="":
        return [s]
    else:
        leftShuffle=[s[0]+val for val in shuffle(s[1:],t)]
        rightShuffle=[t[0]+val for val in shuffle(s,t[1:])]
        leftShuffle.extend(rightShuffle)
        return leftShuffle

Зразок IO:

shuffle("$", "1234"):
['$1234', '1$234', '12$34', '123$4', '1234$']

shuffle("az", "by"):
['azby', 'abzy', 'abyz', 'bazy', 'bayz', 'byaz']

shuffle("code", "golf"):
['codegolf', 'codgeolf', 'codgoelf', 'codgolef', 'codgolfe', 'cogdeolf', 'cogdoelf',
'cogdolef', 'cogdolfe', 'cogodelf', 'cogodlef', 'cogodlfe', 'cogoldef', 'cogoldfe',
'cogolfde', 'cgodeolf', 'cgodoelf', 'cgodolef', 'cgodolfe', 'cgoodelf', 'cgoodlef',
'cgoodlfe', 'cgooldef', 'cgooldfe', 'cgoolfde', 'cgoodelf', 'cgoodlef', 'cgoodlfe',
'cgooldef', 'cgooldfe', 'cgoolfde', 'cgolodef', 'cgolodfe', 'cgolofde', 'cgolfode',
'gcodeolf', 'gcodoelf', 'gcodolef', 'gcodolfe', 'gcoodelf', 'gcoodlef', 'gcoodlfe',
'gcooldef', 'gcooldfe', 'gcoolfde', 'gcoodelf', 'gcoodlef', 'gcoodlfe', 'gcooldef',
'gcooldfe', 'gcoolfde', 'gcolodef', 'gcolodfe', 'gcolofde', 'gcolfode', 'gocodelf',
'gocodlef', 'gocodlfe', 'gocoldef', 'gocoldfe', 'gocolfde', 'goclodef', 'goclodfe',
'goclofde', 'goclfode', 'golcodef', 'golcodfe', 'golcofde', 'golcfode', 'golfcode']

Як завжди, застосовуються стандартні лазівки, і найкоротша відповідь у байтах виграє. Оскільки питання спочатку стосувалося python, я хотів би побачити найкоротший відповідь python. (І ні, піт - не пітон). Однак відповіді будь-якою мовою рекомендуються.


5
Найменший байт коду - найкращий спосіб зробити це, кожен це знає! * (Відмова від відповідальності: не CR).
Rɪᴋᴇʀ

1
Чи всі персонажі відрізняються? Або не обов’язково?
aditsu

4
Насправді ... у вашому прикладі "коду", "гольфу" у вас є дублікат "o" і дублікат результатів, наприклад, "gcoodelf". Я припускаю, що ви цього хочете.
aditsu

1
"Я щойно знайшов це чудове питання. Однак є одна фатальна вада: вони хочуть, щоб це було добре!"
Кіос

1
Ви повинні надати зразок IO для "aabb", "bc".
Taemyr

Відповіді:


1

Піта, 26

M?G?H++LhGgtGH+LhHgGtH]G]H

Спробуйте тут

Це дуже основна реалізація заданої рекурсивної формули. Він визначає функцію, gяка виконує необхідне завдання. Посилання - це модифікована програма, яка читає рядки з розділеного рядка STDIN, щоб було зручніше. Для виклику функції виконайте g<string1><string2>.

Розширення:

M                ##  Define a function g taking two arguments: G and H
 ?G?H ... ]G]H   ##  Two ternaries: if G is empty return a list containing H
                 ##  if H is empty return a list containing G
   +             ##  otherwise return these next two lists joined together
   +LhGgtGH      ##  the first letter of G added to each result of a recursive call to g
                 ##  with G missing its first character and H
   +LhHgGtH      ##  the same as above but with G and H swapped

Два рекурсивні дзвінки дуже схожі, але я вже не зміг знайти спосіб пограти в них.


10

Haskell, 53 48 байт

a%""=[a]
a%b=[x:t|(x:y,z)<-[(a,b),(b,a)],t<-y%z]

Визначає функцію, %для якої за a%bдопомогою рядків a,bнадається список рядків.

З урахуванням двох рядків, ми обираємо одну з двох, з якої беремо перший символ. Потім повторюємо решту двох рядків, попередньо додаючи цей символ до кожного результату.

Коли одна з рядків порожня, єдиний можливий результат - інший рядок. ""%""=[""]Було б також достатньо, але це довше.


53 байти:

a@(b:c)%d@(e:f)=((b:)<$>c%d)++((e:)<$>a%f)
a%d=[a++d]

Визначає функцію, %для якої за a%dдопомогою рядків a,dнадається список рядків.

Функція визначається рекурсивно. Якщо ми беремо символ з першого рядка, то він повинен бути попередженим до кожного результату рекурсивного виклику, що залишився від першого рядка з другим рядком. Симетрично для іншого рядка.

Для базового випадку, якщо один з рядків порожній, результат є одноелементним списком їх конкатенації. Це коротше, ніж два випадки, коли кожен рядок порожній.


@aditsu На жаль, я мав на увазі ""%""=[""].
xnor

Дивно, щоб відповідь перемогла вас точно на один байт точно такою ж мовою
гордий haskeller

10

Хаскелл, 47

(x:s)#b=(x:)<$>s%b
a#b=[]
[]%b=[b]
a%b=a#b++b#a

% є оператором, який вирішує цю проблему.

#це оператор, який бере два списки і знаходить усі способи їх переплетення таким чином, щоб перший символ був з першої рядки (з кращим регістром - якщо перший список порожній, то результат є порожнім списком), повторюючи %.

тоді, %працює лише #два рази.

Редагувати: У попередній версії була помилка, в яку ""%""повернувся ["",""], тому я її виправив. Це було виправлено додаванням базового корпусу до %, який потім дозволив видалити базовий корпус однакової довжини з #(що насправді не мало особливого сенсу).


@nimi Але типи розбіжні - (#) :: [a]->[a]->[[a]]значить, a::[a]і результат повинен бути [[a]]
типовим

На жаль, ви праві. Вибачте.
німі

8

Python 2, 71 байт

f=lambda*p:[x[0]+t for x,y in p,p[::-1]for t in x and f(x[1:],y)]or['']

Приклад виконання:

>> f('ab','AB')
['abAB', 'aABb', 'aAbB', 'ABab', 'AabB', 'AaBb']

Враховуючи два рядки, x,yми можемо взяти перший символ xі додати його до кожного результату рекурсивного дзвінка, при цьому його відсутність f(x[1:],y). Або ми можемо зробити те ж саме xі з yпереключеним. Беручи x,yяк вхід, так pі його зворотний `p [:: - 1], ми отримуємо обидві можливості.

Щоб не брати з порожнього рядка x, ми логічно коротке замикання з x and. Якщо обидва рядки порожні, жодна рядок не може бути, xі ми отримуємо і порожній список можливостей, який ми фіксуємо orдо правильного базового випадку [''].

Аналогічна генеративна стратегія в Python 3 (73 байти):

f=lambda p,s='':[f((x[1:],y),s+x[0])for x,y in[p,p[::-1]]if x]or print(s)

Що це за чаклунство ?! (+1)
aditsu

3

Пітона, 80

За запитом, ось відповідь пітона:

f=lambda a,b,c='':[c+x for x in[a+b][a>''<b:]or f(a[1:],b,a[0])+f(a,b[1:],b[0])]

Спасибі Sp3000 за те, що з'їла 4 байти :)


2

CJam, 38

q~L{_2$e&{_2$(\@jf+@@(@@jf++}{+a}?}2jp

Спробуйте в Інтернеті

Динамічне програмування (з використанням запам’ятовуваної рекурсії).

Пояснення:

q~         read and evaluate the input (2 strings)
L{…}2j     calculate with memoized recursion with no initial cache and 2 arguments
  _2$      copy the 2 strings
  e&{…}    if they are both non-empty
    _2$    copy the strings again (they're in reverse order)
    (      take out the first character of the first string
    \@     move the strings after the character
    j      solve recursively
    f+     prepend the character to all results
    @@     bring the other copy of the strings on top (in order)
    (      take out the first character of the second string
    @@     move the strings after the character
    j      solve recursively
    f+     prepend the character to all results
    +      concatenate the 2 sets of results
  {…}      else
    +      concatenate the 2 strings (at least one is empty)
    a      put the result in an array
  ?        end if
p          pretty-print the results for the input strings

2

CJam, 32 байти

qN/_:,eeWf%e~e!\f{\{_2$=(ot}/No}

Перевірте це тут.

Це відчувається по-справжньому гольфуючим, але поки що я знайшов лише 4 альтернативних рішення, які мають однакову кількість байтів:

qN/_ee{),*~}%e!\f{\{_2$=(ot}/No}
l_:!l_0f>@+])e!\f{\{_2$=(ot}/No}
ll__3$:!+.=])e!\f{\{_2$=(ot}/No}
qN/[_:,2,]ze~e!\f{\{_2$=(ot}/No} (found by Sp3000)

Основна ідея полягає в генерації всіх перестановок 0s і 1s, відповідних якій рядку, щоб взяти кожен символ у результаті. Це все, в тому числі і e!. Решта просто виводить символи в тому порядку з двох рядків потім.


Добре, я подумав над цією ідеєю, але не подумав, що може так добре гольфувати.
aditsu

@aditsu Те, що нам насправді потрібно, - це суміш між e*і .*повторює кожен елемент різною кількістю. ;) (Тобто оператор робити :a.*:~. Я вважаю, що це e*можна використати, оскільки він наразі помилки, якщо дано два списки.)
Мартін Ендер

2

JavaScript (Firefox 30-57), 88 84 81 байт

(s,t,g=(v,w)=>v[1]?f(v.slice(1),w).map(x=>v[0]+x):[v+w])=>[...g(s,t),...g(t,s)]

Редагувати: Збережено 4 байти, покращивши умову припинення. Збережено 3 байти завдяки @ edc65.


Надто близько до публікації, але подивіться - це коротше:f=(a,b,z=(v,w)=>v[1]?f(v.slice(1),w).map(x=>v[0]+x):[v+w])=>z(a,b).concat(z(b,a))
edc65

@ edc65 Дуже приємно; Я намагався використати vяк умову, але мені ніколи не приходило в голову v[1].
Ніл

2

Брахілог , 8 байт

p~cᵐz₁cc

Спробуйте в Інтернеті!

Приймає вхід як список двох рядків через вхідну змінну і генерує всі можливі переплетення через вихідну змінну. Оскільки тестові випадки, здається, дозволяють повторювати переплетення, де є спільні листи, я не доклав жодних зусиль, щоб їх уникнути, але це генерує набагато більше дублікатів, а не лише спільних листів. (Якщо це заборонено, але дублікати спільних листів не потрібні, просто додайте три байти, щоб {}ᵘперетворити їх для виведення як список без дублікатів.)

p           A permutation of
            the input variable
   ᵐ        with each element
 ~c         arbitrarily partitioned,
    z       zipped
     ₁      without cycling,
      cc    and concatenated twice
            is the output variable.

По суті, це генерує кожен розділ обох рядків, а потім переплітає їх у звичайному детермінованому порядку в будь-якому порядку. Додаткові дублікати переплетень пояснюються парами розділів, де різниця між довжиною першого і довжиною другої має деяке значення, відмінне від 0 або 1, так що один з них має кінці, які в кінці з'єднуються один з одним. Отже, для отримання виходу з тією ж кратністю, що і вибірка вибірки:

Брахілог , 17 байт

p~cᵐ{lᵐ-ℕ<2&}z₁cc

Спробуйте в Інтернеті!

Додатковий код, {lᵐ-ℕ<2&}не дає жодної пари розділів, де зроблено сторонні поділи. (Я змінив заголовок на TIO, щоб надрукувати лапки, щоб полегшити перевірку виводу в оболонці Python.)


1

MATL , 34 30 байт

h1Mgw~hY@Xu!ttYs*w~tYs1Gn+*+!)

Для цього використовується ідея з цієї відповіді : якщо довжина рядків є mі n, перерахуйте всі m+nбітові шаблони з mвстановленими бітами. Один із способів зробити це перерахуванням: створити всі перестановки вектора за допомогою mодиниць і nнулів, а потім видалити дублікати.

Спробуйте в Інтернеті!

Пояснення

h     % implicitly input the two strings of lengths m and n. Concatenate
1M    % push the two strings again
g     % convert the second strings into ones
w~    % swap. Convert the second string into zeros
h     % concatenate: vector of zeros and ones
Y@    % 2D array with all permutations of that vector, each on a row
Xu    % remove duplicate rows
!     % transpose
ttYs  % duplicate twice. Cumulative sum along each column
*     % element-wise product. Produces, in each column, indices for
      % elements of the first string; 1, 2,...,m. The rest are 0
w~    % swap, negate
tYs   % duplicate. Cumulative sum along each column
1Gn+  % add length of first input
*     % element-wise product. Produces, in each column, indices for
      % elements of the second string: m+1,...,m+n. The rest are 0
+     % add. This gives indices into the concatenated string created initially
!     % transpose back
)     % index into concatenated string. Implicitly display

0

Рубін, 83 байти

Рекурсивна функція, яка повертається, [a+b]якщо будь-який з цих рядків порожній. В іншому випадку він повертає список рядків, a[0] + every string in v[a[1..-1],b]доданих до списку рядківb[0] + every string in v[a,b[1..-1]]

v=->a,b{a[0]&&b[0]?v[a[1..-1],b].map{|i|a[0]+i}+v[a,b[1..-1]].map{|i|b[0]+i}:[a+b]}

0

Пакетна, 154 152 байти

@if "%~1%~2"=="" echo %3
@set t=%~1
@if not "%t%"=="" call %0 "%t:~1%" "%~2" %3%t:~,1%
@set t=%~2
@if not "%t%"=="" call %0 "%~1" "%t:~1%" %3%t:~,1%
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.