Rétine , 61 55 octets
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$
Étant donné qu'il ne s'agit que d'une seule expression régulière, Retina s'exécutera en mode Match et signalera le nombre de correspondances trouvées, qui sera 1
valable pour les séquences valides et 0
autres. Ce n'est pas compétitif par rapport aux langues de golf, mais j'en suis assez content, vu que j'ai commencé avec un monstre de 260 octets.
Explication
^((.)(?<!\2.+))*
Ce bit consomme un préfixe de lettres uniques de longueur variable, c'est-à-dire qu'il correspond au bloc de tête potentiellement incomplet. Le lookbehind garantit qu'aucun caractère correspondant à ce bit n'apparaît dans la chaîne auparavant.
Maintenant, pour le reste de l'entrée, nous voulons faire correspondre des morceaux de 7 sans répéter les caractères. Nous pourrions correspondre à un tel morceau comme ceci:
(.)(?!.{0,5}\1)(.)(?!.{0,4}\2)(.)(?!.{0,3}\3)...(.)(?!.?\5).
C'est-à-dire que nous faisons correspondre un caractère qui n'apparaît pas pour 6 autres caractères, puis un qui n'apparaît pas pour 5 autres caractères et ainsi de suite. Mais cela nécessite une répétition de code assez horrible, et nous devons faire correspondre séparément un fragment de fin (potentiellement incomplet).
Équilibrer les groupes à la rescousse! Une façon différente de faire correspondre
(.)(?!.{0,5}\1)
consiste à pousser 5 allumettes vides sur une pile de capture et essayez de la vider:
(){5}(.)(?!(?<-1>.)*\2)
Le *
permet un minimum de zéro répétitions, tout comme {0,5}
, et comme nous avons poussé cinq captures, il ne pourra pas apparaître plus de 5 fois non plus. C'est plus long pour une seule instance de ce modèle, mais c'est beaucoup plus réutilisable. Étant donné que nous effectuons le saut dans une anticipation négative , cela n'affecte pas la pile réelle une fois la anticipation terminée. Donc, après l'anticipation, nous avons encore 5 éléments sur la pile, peu importe ce qui s'est passé à l'intérieur. De plus, nous pouvons simplement extraire un élément de la pile avant chaque anticipation, et exécuter le code en boucle, pour décrémenter automatiquement la largeur de l'anticipation de 5 à 0. Donc, ce très long bit là-bas peut en fait être raccourci en
(){7}((?<-1>)(.)(?!(?<-1>.)*\1\3))*
(Vous pouvez remarquer deux différences: nous poussons 7 au lieu de 5. Une capture supplémentaire est parce que nous sautons avant chaque itération, pas après. L'autre est en fait nécessaire pour que nous puissions sortir de la pile 7 fois (puisque nous voulons la boucle doit être exécutée 7 fois), nous pouvons corriger cette erreur de coup par coup à l'intérieur de l'anticipation en s'assurant \1
qu'il reste encore au moins un élément sur la pile.)
La beauté de cela est qu'il peut également correspondre au morceau incomplet de fin, car nous ne l'avons jamais exigé pour répéter 7 fois (c'est juste le maximum nécessaire, car nous ne pouvons pas sortir de la pile plus souvent que cela). Donc, tout ce que nous devons faire est de l'enrouler dans une autre boucle et de nous assurer que nous avons atteint la fin de la chaîne pour obtenir
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$