Conséquences générales


11

introduction

Dans ce défi, votre tâche consiste à trouver des sous-séquences généralisées de chaînes. Les sous-séquences ne sont pas nécessairement contiguës, et elles peuvent également "boucler" la chaîne, en dépassant sa fin et en recommençant depuis le début. Vous voudrez cependant minimiser le nombre de tours.

Plus formellement, laissez uet vsoyez deux chaînes quelconques et k ≥ 0un entier. Nous disons que uc'est une ksous-séquence enveloppante de v, s'il existe des indices distincts tels que , et au plus des indices satisfont . Cela signifie que vous pouvez le trouver à l'intérieur en allant de gauche à droite, en choisissant certains de ses personnages en chemin et en vous déplaçant la plupart du temps (de manière équivalente, en faisant tout au plus des balayages ). Notez qu'aucun caractère ne peut être choisi plus d'une fois, même après un bouclage , et que les sous-séquences -wrapping sont exactement les sous-séquences ordinaires que nous connaissons tous.i1, i2, ..., ilen(u)u == v[i1] v[i2] ... v[ilen(u)]kijij > ij+1uvkk+1v0

La tâche

Vos entrées sont deux chaînes alphanumériques non vides uet v, et votre sortie est le plus petit entier ktel que ula ksous-séquence d'habillage v. S'il n'en kexiste pas, la sortie doit être -1.

Exemple

Considérez les entrées u := xyzyxzzxyxet v := yxzzazzyxxxyz. Si nous commençons à chercher les personnages de ude vmanière gourmande, nous allons envelopper 3 fois:

 yxzzazzyxxxyz
>─x─────y────z┐
┌─────────────┘
└y───────x────┐
┌─────────────┘
└──zz─────x─y─┐
┌─────────────┘
└──────────x──>

Ainsi, la sortie correcte est au maximum de 3. Notez comment le caractère le plus à gauche xest sélectionné une fois, puis ignoré lors du deuxième balayage, car il ne peut pas être réutilisé. Cependant, il existe une méthode plus courte avec seulement 2 bouclages:

 yxzzazzyxxxyz
>──────────xyz┐
┌─────────────┘
└yxzz────x────┐
┌─────────────┘
└───────y─x───>

Il s'avère qu'un bouclage (c'est-à-dire deux balayages) n'est pas suffisant, donc la sortie correcte est 2.

Règles et bonus

Vous pouvez écrire une fonction ou un programme complet, et vous pouvez également modifier l'ordre des entrées si nécessaire. Le nombre d'octets le plus bas gagne et les failles standard sont interdites.

Il y a un bonus de -10% pour le calcul de tous les cas de test en moins de 10 secondes au total. Je vais tester des cas peu clairs sur ma machine; mon implémentation de référence en Python prend environ 0,6 seconde. J'ai un ordinateur portable de 7 ans avec un processeur dual core 1,86 GHz, dont vous voudrez peut-être tenir compte.

Cas de test

"me" "moe" -> 0
"meet" "metro" -> -1
"ababa" "abaab" -> 1
"abaab" "baabaa" -> 1
"1c1C1C2B" "1111CCCcB2" -> 3
"reverse" "reserved" -> 2
"abcdefg" "gfedcba" -> 6
"xyzyxzzxyx" "yxzzazzyxxxyz" -> 2
"aasdffdaasdf" "asdfddasdfsdaafsds" -> 2

1
Serait-ce également une solution valable pour l'exemple? C'est une approche gourmande.
orlp

@orlp Il n'est pas valide, car le premier xest utilisé dans trois balayages distincts. Il ne peut être utilisé qu'une seule fois.
Zgarb

Ahhh, je vois maintenant.
orlp

Réponses:


4

Pyth, 34 octets

Mh+Smssm>.ukC,dtdfqGsm@HkT.PUHlG_1

Ceci définit une fonction g, qui prend deux chaînes en paramètre. Essayez-le en ligne: Pyth Compiler / Executor

Ce code est très inefficace. Il a une complexité de temps et de mémoire de len(v)!/(len(v)-len(u))!. Il n'est pas en mesure de résoudre les cas de test plus longs en moins de 10 secondes. (Il se bloquera également très probablement, car il manquera de mémoire.)

M                                    define g(G, H): return _
                          .PUHlG        all permutations of [0, 1, ..., len(H)-1] of length len(G)
                 fqGsm@HkT              filter the permutations which form the string G
    mssm>.ukC,dtd                       compute the number of wraps for each of the remaining permutations
  +S                            _1      sort the numbers and append -1
 h                                      return the first element

4

Haskell, 160 * 0,9 = 144 octets

a#(-1)=a
a#b=min a b
f y=w(y++" ")0$length y
w _ n _[]=n
w(c:d)n o g@(a:b)|n>o=(-1)|a==c=z#w y n z g|c==' '=w y(n+1)o g|1<2=w y n o g where z=w d n o b;y=d++[c]

Timing pour tous les cas de test (remarque: les arguments sont inversés):

*Main> map (uncurry f) [
             ("moe", "me"),
             ("metro", "meet"),
             ("abaab", "ababa"),
             ("baabaa", "abaab"),
             ("1111CCCcB2", "1c1C1C2B"),
             ("reserved", "reverse"),
             ("gfedcba", "abcdefg"),
             ("yxzzazzyxxxyz", "xyzyxzzxyx"),
             ("asdfddasdfsdaafsds", "aasdffdaasdf")]
[0,-1,1,1,3,2,6,2,2]
(0.08 secs, 25794240 bytes)

Comment ça marche (version courte): simple force brute qui prend le minimum d'utiliser un caractère correspondant et de le sauter. J'arrête la recherche lorsque vous avez terminé (retour du nombre de cycles) ou lorsque vous avez fait plus de vélo que le minimum jusqu'à présent (retour -1).

Économisé beaucoup d'octets par rapport à ma première version, principalement parce que je suis passé d'un programme complet à une fonction.

Avec quelques commentaires et un espacement approprié, Haskell est assez lisible:

-- a minimum function that ignores a -1 in the right argument to prevent
-- "not solvable" cases in parts of the recursive search to dominate low numbers
-- of solvable parts. If the case isn't solvabale at all, both arguments are
-- -1 and are carried on.
a # (-1) = a
a # b    = min a b

-- the main function f calls the worker funktion w with arguments
-- * the string to search in (STSI), appended by a space to detect cycles
-- * the number of cycles so far
-- * the minimum of cycles needed so far, starting with the length of STSI
-- * the string to search for (STSF) (partial applied away and therefore invisible)
f y = w (y++" ") 0 (length y)

-- the worker function 
w _ n _ [] = n          -- base case: if STSF is empty the work is done and the 
                        -- number of cycles is returned

w (c:d) n o g@(a:b)     -- "c" is first char of STSI, "d" the rest
                        -- "n" number of cycles, "o" minimum of cycles so far
                        -- "g" is the whole STSF, "a" the 1st char, "b" the rest
  | n>o    = (-1)             -- if current cycle is more than a previous result,
                              -- indicate failure
  | a==c   = z # w y n z g    -- if there's a character match, take the min of
                              -- using it and skipping it
  | c==' ' = w y (n+1) o g    -- cycle detected, repeat and adjust n
  | 1<2    = w y n o g        -- otherwise try next char in STSI

  where                 -- just some golfing: short names for common subexpressions
  z = w d n o b;        -- number of cycles if a matching char is used
  y = d ++ [c]          -- rotated STSI

Pour référence: ancienne version, programme complet, 187 octets

main=interact$show.f.lines
a#(-1)=a
a#b=min a b
f[x,y]=w x(y++" ")0 0
w[]_ n _=n
w g@(a:b)(c:d)n m|a==c=w b d n 1#y|c==' '&&m==1=w g(d++" ")(n+1)0|c==' '=(-1)|1<2=y where y=w g(d++[c])n m

@Zgarb: retravaillé ma solution. C'est maintenant plus rapide et plus court.
nimi

Fonctionne en 0,6 s lors de l'interprétation, 0,01 s lors de la compilation.
Zgarb

2

JavaScript (ES6) 174 (193 - 10%)

Recherche récursive, comme la réponse de @ nimi, en gardant le minimum de tours. L'espace des solutions est grand (surtout pour le dernier exemple) mais couper la recherche au min actuellement trouvé permet de garder le temps bas. Edit 1 Ajoutez un cas de test manquant, raccourci un peu Edit 2 Pas besoin de passer le paramètre w around, c'est corrigé

K=(w,s,x)=>
  ~-(R=(r,l,p=0,q=1,z=w[p],i=0)=>
  {
    if(z&&!(q>x)){
      if(~(r+l).indexOf(z))
        for(t=l?R(l+r,'',p,q+1):x;x<t?0:x=t,i=~r.indexOf(z,-i);)
          t=R(r.slice(-i),l+r.slice(0,~i),p+1,q);
      q=x
    }
    return q
  })(s,'')

Non golfé

K=(word, astring)=>
{
  var minWraps // undefined at first. All numeric comparison with undefined give false 
  var R=(right, left, pos, wraps)=>
  {
    var cur = word[pos]
    var i,t;
    if (! cur) // when all chars of word are managed
      return wraps;
    if (wraps > minWraps) // over the minimum wrap count already found, stop search
      return wraps; 
    if ( (right+left).indexOf(cur) < 0 ) // if the current char is not found in the remaining part of the string
      return minWraps; // return the current min, could still be undefined (that means 'no way')
    if ( left ) // if there is a left part, try a wrapping search with the current char
    {
      t = R(left+right, '', pos, wraps+1)
      if ( !(minWraps < t)) minWraps = t; // set current min if t is less than current min or current min is still undefined
    }
    // find all occurrences of current char in the remaining part
    // for each occurrence, start a recursive search for the next char
    for(i = 0; (i = right.indexOf(cur, i)) >= 0; i++)
    {
      var passed = right.slice(0,i) // the passed chars go in the left part
      var rest = right.slice(i+1) 
      t = R(rest, left+passed, pos+1, wraps) // try next char in the remaining part, no wrap
      if ( !(minWraps < t)) minWraps = t; // set current min if t is less than current min or current min is still undefined
    }
    return minWraps
  }
  var result = R(astring, '', 0, 1) // start with right=string and left empty
  return ~-result; // decrement. convert undefined to -1
}

Tester dans la console Firefox / FireBug

time=~new Date;
[['me','moe']
,['meet','metro']
,['ababa','abaab']
,['abaab','baabaa']
,['1c1C1C2B','1111CCCcB2']
,['reverse','reserved']
,['abcdefg','gfedcba']
,['xyzyxzzxyx','yxzzazzyxxxyz']
,['aasdffdaasdf','asdfddasdfsdaafsds']]
.forEach(s=>console.log(s,r=K(...s)))
time-=~new Date

Sortie (la dernière ligne est le temps d'exécution en ms)

["moi", "moe"] 0
["rencontrer", "metro"] -1
["ababa", "abaab"] 1
["abaab", "baabaa"] 1
["1c1C1C2B", "1111CCCcB2"] 3
["reverse", "réservé"] 2
["abcdefg", "gfedcba"] 6
["xyzyxzzxyx", "yxzzazzyxxxyz"] 2
["aasdffdaasdf", "asdfddasdfsdaafsds"] 2
116


Testé avec Firebug, fonctionne en 175ms sur ma machine.
Zgarb

@Zgarb alors il y a de la place pour des améliorations: je vais essayer de le rendre plus lent et plus court
edc65
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.