Retina , 61 55 byte
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$
Poiché questo è solo un singolo regex, Retina verrà eseguito in modalità Match e riporterà il numero di match trovati, che sarà 1
per sequenze valide e in 0
altro modo. Questo non è competitivo rispetto alle lingue del golf, ma ne sono abbastanza contento, visto che ho iniziato con un mostro di 260 byte.
Spiegazione
^((.)(?<!\2.+))*
Questo bit consuma un prefisso di lettere univoche di lunghezza variabile, ovvero corrisponde al blocco iniziale potenzialmente incompleto. Il lookbehind assicura che nessun carattere abbinato in questo bit non sia mai apparso nella stringa prima.
Ora per il resto dell'input, vogliamo abbinare pezzi di 7 senza ripetere i caratteri. Potremmo abbinare un pezzo del genere in questo modo:
(.)(?!.{0,5}\1)(.)(?!.{0,4}\2)(.)(?!.{0,3}\3)...(.)(?!.?\5).
Cioè abbiniamo un personaggio che non appare per altri 6 caratteri, quindi uno che non appare per altri 5 caratteri e così via. Ma questo richiede una ripetizione del codice abbastanza orribile, e dovremmo abbinare separatamente un pezzo finale (potenzialmente incompleto).
Equilibrare i gruppi in soccorso! Un modo diverso di abbinare
(.)(?!.{0,5}\1)
è spingere 5 partite vuote su uno stack di acquisizione e provare a svuotarlo:
(){5}(.)(?!(?<-1>.)*\2)
La *
permette un minimo di zero ripetizioni, proprio come {0,5}
, e perché abbiamo spinto cinque acquisizioni, non sarà in grado di pop più di 5 volte neanche. Questo è più lungo per una singola istanza di questo modello, ma è molto più riutilizzabile. Dato che stiamo facendo scoppiare un lookahead negativo , questo non influisce sullo stack effettivo una volta completato il lookahead. Quindi dopo il lookahead, abbiamo ancora 5 elementi in pila, non importa cosa sia successo all'interno. Inoltre, possiamo semplicemente estrarre un elemento dallo stack prima di ogni lookahead ed eseguire il codice in un ciclo, per ridurre automaticamente la larghezza del lookahead da 5 fino a 0. In modo che un bit veramente lungo lassù possa effettivamente essere ridotto a
(){7}((?<-1>)(.)(?!(?<-1>.)*\1\3))*
(Potresti notare due differenze: stiamo spingendo 7 invece di 5. Un'acquisizione aggiuntiva è perché saltiamo prima di ogni iterazione, non dopo di essa. L'altra è effettivamente necessaria in modo che possiamo espellere dallo stack 7 volte (poiché vogliamo l'esecuzione del ciclo 7 volte), possiamo correggere l'errore off-by-one all'interno del lookahead assicurandoci \1
che rimanga almeno un elemento nello stack.)
Il bello di questo è che può eguagliare anche il pezzo incompleto finale, perché non abbiamo mai richiesto che si ripetesse 7 volte (questo è solo il massimo necessario, perché non possiamo saltar fuori dallo stack più spesso di così). Quindi tutto ciò che dobbiamo fare è avvolgerlo in un altro loop e assicurarci di aver raggiunto la fine della stringa
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$