Все возможные способы чередования двух строк


21

Я недавно видел этот вопрос на stackoverflow. Это отличный вопрос, но есть одна фатальная проблема с этим вопросом. Они просят лучшего способа сделать это. Например, самый легкий для чтения, самый идиоматичный, самый красивый и т. Д. Разве они не знают, что это не главное? Вы должны спросить о том, как сделать это с наименьшим количеством байтов кода!

Поскольку я сомневаюсь, что этот вопрос будет оценен по 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

Образец ввода-вывода:

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

Как обычно, применяются стандартные лазейки, и выигрывает самый короткий ответ в байтах. Поскольку вопрос изначально был о питоне, я бы хотел увидеть самый короткий ответ по питону. (И нет, пит - это не питон). Тем не менее, ответы на любом языке приветствуются.


5
Наименьшее количество байтов кода - лучший способ сделать это, каждый знает это! * (Отказ от ответственности: не CR).
Rɪᴋᴇʀ

1
Все персонажи разные? Или не обязательно?
aditsu

4
На самом деле ... в вашем примере "code", "golf" у вас есть дубликат "o" и дублированные результаты, например, "gcoodelf". Я предполагаю, что это то, что вы хотите.
aditsu

1
«Я только что нашел этот замечательный вопрос. Однако есть один фатальный недостаток: они хотят, чтобы все было хорошо!»
Cyoce

1
Вы должны предоставить пример ввода / вывода для "aabb", "bc".
Таемир

Ответы:


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]]
типичным

Ой, ты прав. Сожалею.
Nimi

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

Python, 80

Как и просили, вот ответ Python:

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*Можно было бы использовать для этого , так как он в настоящее время ошибок , если даны два списка).
Martin Ender

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

Попробуйте онлайн!

Принимает input как список из двух строк через входную переменную и генерирует все возможные чередования через выходную переменную. Поскольку контрольные примеры, по-видимому, допускают дублирующиеся чередования, когда есть общие буквы, я не позаботился о том, чтобы их избежать, но это создает намного больше дубликатов, а не только с общими буквами. (Если это не разрешено, но общие дубликаты букв не нужны, просто добавьте три байта для переноса {}ᵘдля вывода в виде списка без дубликатов.)

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.