Toutes les manières possibles d'entrelacer deux chaînes


21

J'ai récemment vu cette question sur stackoverflow. C'est une excellente question, mais il y a un problème fatal avec la question. Ils demandent la meilleure façon de le faire. Par exemple, les plus faciles à lire, les plus idiomatiques, les plus soignés, etc. Ne savent-ils pas que ce n'est pas ce qui compte? Vous êtes censé demander comment le faire avec le moins d'octets de code!

Comme je doute que cette question soit appréciée sur stackoverflow, j'ai décidé de la poser ici.

Le défi

Vous devez écrire le programme ou la fonction la plus courte possible qui génère toutes les manières possibles d'entrelacer deux chaînes arbitraires. Par exemple, si les deux chaînes sont 'ab'et 'cd', la sortie est:

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

Comme vous pouvez le voir, ac'est toujours avant b, et cc'est toujours avant d.

IO peut être dans n'importe quel format raisonnable. Utilisez ce code python pour vérifier et vérifier votre sortie. (crédit: 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

Exemple d'E / S:

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

Comme d'habitude, les failles standard s'appliquent et la réponse la plus courte en octets l'emporte. Puisque la question portait à l'origine sur python, j'aimerais voir la réponse la plus courte en python. (Et non, pyth n'est pas python). Cependant, les réponses dans n'importe quelle langue sont encouragées.


5
Le moins d'octets de code est la meilleure façon de le faire, tout le monde le sait! * (Avertissement: pas CR).
Rɪᴋᴇʀ

1
Tous les personnages sont-ils distincts? Ou pas nécessairement?
aditsu

4
En fait ... dans votre "code", exemple "golf" vous avez un "o" en double et des résultats en double aussi, par exemple "gcoodelf". Je suppose que c'est ce que tu veux.
aditsu

1
"Je viens de trouver cette grande question. Cependant, il y a une faille fatale: ils veulent que ce soit bien fait!"
Cyoce

1
Vous devez fournir l'exemple d'E / S pour "aabb", "bc".
Taemyr

Réponses:


1

Pyth, 26

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

Essayez-le ici

Il s'agit d'une implémentation très basique de la formule récursive donnée. Il définit une fonction gqui exécute la tâche requise. Le lien est un programme modifié qui lit les chaînes de la nouvelle ligne STDIN séparément, pour plus de commodité. Pour appeler la fonction, faites g<string1><string2>.

Expansion:

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

Les deux appels récursifs sont très similaires, mais je n'ai plus réussi à les jouer au golf.


10

Haskell, 53 48 octets

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

Définit une fonction %pour laquelle a%bavec des chaînes a,bdonne une liste de chaînes.

Étant donné deux chaînes, nous choisissons l'une des deux pour prendre le premier caractère. Nous répétons ensuite le reste des deux chaînes, en ajoutant ce caractère à chaque résultat.

Lorsque l'une des chaînes est vide, le seul résultat possible est l'autre chaîne. ""%""=[""]serait également suffisant, mais c'est plus long.


53 octets:

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

Définit une fonction %pour laquelle a%davec des chaînes a,ddonne une liste de chaînes.

La fonction est définie récursivement. Si nous prenons un caractère de la première chaîne, il doit être ajouté à chaque résultat de l'appel récursif sur le reste de la première chaîne avec la deuxième chaîne. Symétriquement pour l'autre chaîne.

Pour le cas de base, si l'une des chaînes est vide, le résultat est une liste à un élément de leur concaténation. C'est plus court que deux cas pour chaque chaîne étant vide.


@aditsu Oups, je voulais dire ""%""=[""].
xnor

C'est bizarre d'avoir une réponse qui vous gagne exactement d'un octet dans la même langue
fier haskeller

10

Haskell, 47

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

% est l'opérateur qui résout ce défi.

#est un opérateur qui prend deux listes et trouve toutes les façons de les entrelacer de telle sorte que le premier caractère soit de la première chaîne (avec un cas de bord - si la première liste est vide, alors le résultat est une liste vide) en récursif à %.

puis, %fonctionne en appliquant simplement #deux fois.

Edit: La version précédente avait un bug dans lequel ""%""retourné ["",""], donc je l'ai corrigé. Il a été corrigé en ajoutant un cas de base à %, ce qui a ensuite permis de supprimer un cas de base de la même longueur #(ce qui n'avait vraiment pas beaucoup de sens).


@nimi Mais les types ne correspondent pas - (#) :: [a]->[a]->[[a]], donc a::[a]et le résultat devrait être de type[[a]]
fier haskeller

Oups, vous avez raison. Pardon.
nimi

8

Python 2, 71 octets

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

Exemple d'exécution:

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

Étant donné deux chaînes, x,ynous pouvons prendre le premier caractère de xet l'ajouter à chaque résultat de l'appel récursif manquant f(x[1:],y). Ou, nous pouvons faire de même avec xet ycommuté. En prenant x,ysoit l'entrée psoit son inversion `p [:: - 1], nous obtenons les deux possibilités.

Pour éviter de prendre dans une chaîne vide x, on court-circuite logiquement avec x and. Si les deux chaînes sont vides, aucune chaîne ne peut l'être xet nous obtenons une liste de possibilités vide, que nous corrigeons avec orle cas de base correct [''].

Une stratégie générative similaire en Python 3 (73 octets):

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

Quel genre de sorcellerie est ce?! (+1)
aditsu

3

Python, 80

Comme demandé, voici une réponse en 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])]

Merci Sp3000 pour avoir mangé 4 octets :)


2

CJam, 38

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

Essayez-le en ligne

Programmation dynamique (utilisant la récursion mémorisée).

Explication:

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 octets

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

Testez-le ici.

Cela semble vraiment jouable au golf, mais jusqu'à présent, je n'ai trouvé que 4 solutions alternatives qui ont toutes le même nombre d'octets:

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)

L'idée de base est de générer toutes les permutations de 0s et 1s correspondant à quelle chaîne prendre chaque caractère du résultat. C'est tout jusqu'à et y compris le e!. Le reste extrait simplement les caractères dans cet ordre des deux chaînes.


Bien, j'ai pensé à cette idée, mais je ne pensais pas que ça pouvait si bien jouer au golf.
aditsu

@aditsu Ce dont nous avons vraiment besoin, c'est d'un mélange entre e*et .*qui répète chaque élément d'une quantité différente. ;) (Soit un opérateur de faire :a.*:~je suppose. e*Pourrait être utilisé pour cela car elle actuellement des difficultés si on leur donne deux listes).
Martin Ender

2

JavaScript (Firefox 30-57), 88 84 81 octets

(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)]

Edit: sauvé 4 octets en améliorant ma condition de terminaison. Sauvegardé 3 octets grâce à @ edc65.


Trop près pour publier, mais jetez un œil - c'est plus court: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 Très agréable; J'avais essayé et échoué à utiliser vcomme condition, mais il ne m'est jamais venu à l'esprit de l'utiliser v[1].
Neil

2

Brachylog , 8 octets

p~cᵐz₁cc

Essayez-le en ligne!

Prend l'entrée sous la forme d'une liste de deux chaînes via la variable d'entrée et génère tous les entrelacements possibles via la variable de sortie. Étant donné que les cas de test semblent permettre des entrelacements en double là où il y a des lettres partagées, je n'ai pas pris soin de les éviter, mais cela génère beaucoup plus de doublons et pas seulement avec des lettres partagées. (Si cela n'est pas autorisé, mais que les doublons de lettres partagées ne sont pas nécessaires, ajoutez simplement trois octets pour envelopper la {}ᵘsortie sous forme de liste sans doublons.)

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.

Essentiellement, cela génère chaque partition des deux chaînes, puis les entrelace de la manière déterministe normale dans l'un ou l'autre ordre. Les entrelacements en double supplémentaires sont dus à des paires de partitions où la différence entre la longueur du premier et la longueur du second a une valeur autre que 0 ou 1, de sorte que l'un d'eux a des morceaux qui se concaténent à la fin. Donc, pour produire une sortie avec les mêmes multiplicités que la sortie échantillon:

Brachylog , 17 octets

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

Essayez-le en ligne!

Le code supplémentaire,, {lᵐ-ℕ<2&}échoue à toute paire de partitions où des divisions étrangères sont effectuées. (J'ai modifié l'en-tête sur TIO pour l'imprimer avec des guillemets pour une vérification plus facile des sorties dans le shell Python.)


1

MATL , 34 30 octets

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

Cela utilise une idée de cette réponse : si les longueurs des chaînes sont met n, énumérer tous m+nles modèles de mbits avec des bits définis. Une façon de faire cette énumération est de générer toutes les permutations d'un vecteur avec des muns et des nzéros, puis de supprimer les doublons.

Essayez-le en ligne!

Explication

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

Rubis, 83 octets

Une fonction récursive qui retourne [a+b]si l'une de ces chaînes est vide. Sinon, il renvoie une liste de chaînes a[0] + every string in v[a[1..-1],b]ajoutées à une liste de chaînesb[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

Lot, 154 152 octets

@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%
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.