Périodes locales de chaînes


20

Périodes locales

Prenez une chaîne non vide s . La période locale de s à l'indice i est le plus petit entier positif n tel que pour chaque 0 ≤ k <n , nous avons s [i + k] = s [i-n + k] chaque fois que les deux côtés sont définis. Alternativement, c'est la longueur minimale d'une chaîne non vide w telle que si la concaténation ww est placée à côté de s de sorte que la deuxième copie de w commence à l'index i de s , alors les deux chaînes s'accordent partout où elles se chevauchent.

À titre d'exemple, calculons la période locale de s = "abaabbab" à l'index 2 (basé sur 0).

  • Essayez n = 1 : puis s [2 + 0] ≠ s [2-1 + 0] , donc ce choix n'est pas correct.
  • Essayez n = 2 : alors s [2 + 0] = s [2-2 + 0] mais s [2 + 1] ≠ s [2-2 + 1] , ce n'est donc pas correct non plus.
  • Essayez n = 3 : alors s [2 + 0-3] n'est pas défini, s [2 + 1] = s [2-3 + 1] et s [2 + 2] = s [2-3 + 2] . La période locale est donc de 3.

Voici une visualisation des périodes locales en utilisant la deuxième définition, avec des points-virgules ajoutés entre les deux copies de w pour plus de clarté:

index      a b a a b b a b      period
 0       a;a                     1
 1       b a;b a                 2
 2       a a b;a a b             3
 3             a;a               1
 4     b b a b a a;b b a b a a   6
 5                 b;b           1
 6               a b b;a b b     3
 7                   b a;b a     2

Notez que w n'est pas nécessairement une sous-chaîne de s . Cela se produit ici dans le cas de l'index-4.

La tâche

Votre entrée est une chaîne non vide s de caractères ASCII minuscules. Il peut être considéré comme une liste de caractères si vous le souhaitez. Votre sortie doit être la liste contenant la période locale de s à chacun de ses indices. Dans l'exemple ci-dessus, la sortie correcte serait [1,2,3,1,6,1,3,2] .

Le nombre d'octets le plus bas dans chaque langue gagne. Les règles de standard s'appliquent.

Cas de test

a -> [1]
hi -> [1, 2]
www -> [1, 1, 1]
xcxccxc -> [1, 2, 2, 5, 1, 3, 2]
abcbacb -> [1, 4, 7, 7, 7, 3, 3]
nininini -> [1, 2, 2, 2, 2, 2, 2, 2]
abaabbab -> [1, 2, 3, 1, 6, 1, 3, 2]
woppwoppw -> [1, 4, 4, 1, 4, 4, 4, 1, 4]
qwertyuiop -> [1, 10, 10, 10, 10, 10, 10, 10, 10, 10]
deededeededede -> [1, 3, 1, 5, 2, 2, 5, 1, 12, 2, 2, 2, 2, 2]
abababcabababcababcabababcaba -> [1, 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 19, 19, 5, 5, 2, 5, 5, 12, 12, 2, 2, 2, 7, 7, 5, 5, 2]

@Arnauld Vous pouvez toujours trouver un w de même longueur que s . Dans le cas de qwertyuiop, w sera une version pivotée de qwertyuiop. Voir aussi l'exemple à l'index 4: w n'est pas nécessairement une sous-chaîne de s .
Zgarb

Ça a du sens. J'ai mal lu le défi.
Arnauld

Bonus imaginaire pour une solution de temps linéaire! (quelqu'un d'autre peut offrir une vraie prime, alors continuez d'essayer)
user202729

Un défi vraiment soigné, mais je me demande s'il serait plus logique de définir la période locale de chaque position entre deux caractères (c'est-à-dire où que se ;trouve dans votre exemple). Cela permettrait de se débarrasser du premier 1.
Martin Ender

@MartinEnder Ce serait conceptuellement plus propre, mais cette définition facilite la production de la sortie en bouclant sur la chaîne, et la sortie ne sera pas vide.
Zgarb

Réponses:


4

Rétine , 89 86 octets

.
$`¶$<'¶
/(^|.+)¶.+/_(Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4
G`.
%C`.
N`
0G`

Essayez-le en ligne! Edit: sauvé 3 octets grâce à @MartinEnder. Explication:

.
$`¶$<'¶

Divisez l'entrée à chaque caractère, en créant une paire de lignes, une pour le préfixe et une pour le suffixe du préfixe.

/(^|.+)¶.+/_(

Exécutez le reste du script sur chaque paire résultante.

Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4

Trouvez toutes les correspondances qui se chevauchent et répertoriez les résultats. (Voir ci-dessous.)

G`.

Jeter le match vide.

%C`.

Prenez la durée de chaque match.

N`

Trier numériquement.

0G`

Prenez le plus petit.

La correspondance fonctionne en divisant le préfixe et le suffixe en trois parties. Il y a quatre cas valides à considérer:

AB|BC   B matches B to the left and B to the right
B|ABC   AB matches [A]B to the left and AB to the right
ABC|B   BC matches BC to the left and B[C] to the right
BC|AB   ABC matches [A]BC to the left and AB[C] to the right

Le regex permet donc uniquement à A et C de correspondre d'un côté à la fois.


$&$'est égal à $<'et la longueur des lignes de calcul est plus courte avec %C`.. tio.run/##K0otycxLNPz/X49LJeHQNhUb9UPbuPQ14mr0tDUPbdPT1o/…
Martin Ender

4

Java 8, 167 154 152 octets

s->{int l=s.length,r[]=new int[l],i=0,n,k;for(;i<l;r[i++]=n)n:for(n=0;;){for(k=++n;k-->0;)if(i+k<l&i+k>=n&&s[i+k]!=s[i-n+k])continue n;break;}return r;}

-2 octets grâce à @ceilingcat .

Essayez-le en ligne.

Explication:

s->{                          // Method with char-array parameter and int-array return-type
  int l=s.length,             //  Length of the input-array
      r[]=new int[l],         //  Result-array of the same size 
      i=0,n,k;                //  Integers `i`, `n`, and `k` as defined in the challenge
  for(;i<l;                   //  Loop `i` in the range [0, `l`):
      r[i++]=n)               //    After every iteration: Add `n` to the array
    n:for(n=0;;){             //   Inner loop `n` from 0 upwards indefinitely
      for(k=++n;k-->0;)       //    Inner loop `k` in the range [`n`, 0]:
                              //    (by first increasing `n` by 1 with `++n`)
        if(i+k<l&i+k>=n)      //     If `i+k` and `i-n+k` are both within bounds,
           &&s[i+k]!=s[i-n+k])//     and if `s[i+k]` is not equal to `s[i-n+k]`:
          continue n;         //      Continue loop `n`
                              //    If we haven't encountered the `continue n` in loop `k`:
      break;}                 //     Break loop `n`
  return r;}                  //  Return the result

1

JavaScript (ES6), 84 octets

Prend l'entrée comme un tableau de caractères.

s=>s.map((_,i)=>s.some(_=>s.every(_=>k<j|!s[k]|s[k-j]==s[k++]|k-i>j,++j,k=i),j=0)*j)

Cas de test


Je ne sais pas si la prise d'un tableau de caractères est autorisée, êtes-vous sûr qu'il ne s'agit pas uniquement de chaînes à 1 caractère?
Erik the Outgolfer le

@EriktheOutgolfer Il n'y a pas de type de caractère dans JS, alors oui: c'est techniquement un tableau de chaînes de 1 caractère. Ma compréhension est que si ça craque comme une chaîne, c'est une chaîne. (Voici un méta-post à ce sujet, mais un autre plus pertinent peut exister - ou celui qui contredit en fait mon hypothèse.)
Arnauld

1
Ou pour le dire autrement: c'est aussi proche que possible d'une liste de caractères dans JS, ce qui était explicitement autorisé par l'OP.
Arnauld

1

Rubis , 104 102 octets

->s{l=s.size-1
(0..l).map{|i|n=0
loop{n+=1
(n-i..l-i).all?{|k|k<0||k>=n||s[i+k]==s[i-n+k]}&&break}
n}}

Essayez-le en ligne!

Un lambda acceptant une chaîne et renvoyant un tableau.

-2 octets: permuter les points de terminaison de plage avec des gardes liés à l'index

Non golfé:

->s{
  l=s.size-1                # l is the maximum valid index into s
  (0..l).map{ |i|           # i is the current index
    n=0                     # n is the period being tested
    loop{                   # Repeat forever:
      n+=1                  # Increment n
      (n-i..l-i).all?{ |k|  # If for all k where i+k and i-n+k are valid indexes into s
        k<0 || k>=n ||      #   We need not consider k OR
          s[i+k]==s[i-n+k]  #   The characters at the relevant indexes match
      } && break            # Then stop repeating
    }
  n                         # Map this index i to the first valid n
  }
}

1

Japt , 33 32 octets

1 octet enregistré grâce à @Shaggy

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ

Testez-le en ligne!

Explication

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ   Implicit: U = input string
¬Ë                                 Split the input into chars, and map each index E to
  @                          }aÄ     the smallest positive integer D where
   ¯E                                  the first E chars of U
      f                                matches the regex formed by
          UtED                         taking D chars of U from index E,
                ú.D                     padding to length D with periods,
                    r."($&|^)"          replacing each char C with "(C|^)",
        '$i                             and placing a '$' at the very end.

Ma première pensée a été de comparer simplement chaque caractère de la sous-chaîne de gauche avec le caractère correspondant dans la sous-chaîne de droite, comme dans la réponse JS. Cela ne fonctionnerait pas, cependant, car la méthode de Japt pour obtenir un caractère passe simplement à l'autre extrémité de la chaîne si l'index est négatif ou trop grand.

Au lieu de cela, ma solution construit une expression régulière à partir de la deuxième sous-chaîne et la teste sur la première sous-chaîne. Prenons le 5ème élément du cas abaabbabde test comme exemple:

abaabbab
    ^ split point -> abaa for testing regex, bbab for making regex

   slice  regex                              matches abaa
1. b      /(b|^)$/                           no
2. bb     /(b|^)(b|^)$/                      no
3. bba    /(b|^)(b|^)(a|^)$/                 no
4. bbab   /(b|^)(b|^)(a|^)(b|^)$/            no
5. bbab.  /(b|^)(b|^)(a|^)(b|^)(.|^)$/       no
6. bbab.. /(b|^)(b|^)(a|^)(b|^)(.|^)(.|^)$/  yes: /^^ab..$/

L'astuce principale est qu'elle ^peut correspondre à l'infini, jusqu'à ce qu'un personnage réel soit mis en correspondance. Cela nous permet d'ignorer n'importe quel nombre de caractères depuis le début de l'expression régulière, tout en garantissant que les autres sont tous mis en correspondance consécutivement, en terminant à la fin de la chaîne de test.

Je ne suis pas sûr d'avoir très bien expliqué cela, alors faites-moi savoir s'il y a quelque chose que vous aimeriez clarifier ou quoi que ce soit qui devrait être expliqué.



@Shaggy Merci, ce point-virgule me
dérangeait

1

C (gcc) , 143 142 140 139 139 128 126 123 octets

  • Enregistré un octet. Golfé !b&&printfà b||printf.
  • Enregistré deux octets grâce à Kevin Cruijssen . Suppression des forparenthèses du corps de la boucle en jonglant avec le printfplacement.
  • Enregistré un octet. Golfé b+=S[i+k]!=S[i-n+k]à b|=S[i+k]-S[i-n+k].
  • Onze octets enregistrés. Suppression de la nécessité de l=strlen(S)conditionner les deux boucles de gestion des chaînes pour qu'elles se rompent lorsqu'elles atteignent la fin de la chaîne (un octet nul '\0').
  • Enregistré deux octets. Golfé i-n+k>~0à i-n>~k.
  • Enregistré trois octets grâce à plafondcat ; b||printf("|"),n++est équivalent à n+=b||printf("|").
i,b,k,n;f(char*S){for(i=~0;S[++i];)for(b=n=1;b;n+=b||printf("%d,",n))for(b=k=0;k<n&&S[i+k];k++)b|=n-i>k?0:S[i+k]-S[i-n+k];}

Essayez-le en ligne!


Vous pouvez économiser 2 octets en supprimant les crochets et en b||printf("%d,",n)insérant la boucle for:i,b,k,n,l;f(char*S){for(l=strlen(S),i=-1;++i<l;)for(b=n=1;b;b||printf("%d,",n),n++)for(b=k=0;k<n;k++)i+k<l&i-n+k>=0&&(b+=S[i+k]!=S[i-n+k]);}
insérant la

@KevinCruijssen Merci.
Jonathan Frech

@ceilingcat Merci; équivalence soignée, celle-là.
Jonathan Frech

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.