Retina , 61 55 байт
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$
Поскольку это всего лишь одно регулярное выражение, Retina будет работать в режиме Match и сообщать о количестве найденных совпадений, что будет 1
для правильных последовательностей и в 0
противном случае. Это неконкурентоспособно по сравнению с языками игры в гольф, но я вполне доволен этим, так как начал с монстра в 260 байт.
объяснение
^((.)(?<!\2.+))*
Этот бит использует префикс уникальных букв переменной длины, то есть соответствует потенциально неполному начальному фрагменту. Заглядывание гарантирует, что любой символ, соответствующий этому биту, ранее не появлялся в строке.
Теперь для остальной части ввода мы хотим сопоставить куски 7 без повторяющихся символов. Мы могли бы сопоставить такой кусок как это:
(.)(?!.{0,5}\1)(.)(?!.{0,4}\2)(.)(?!.{0,3}\3)...(.)(?!.?\5).
Т.е. мы сопоставляем символ, который не отображается для других 6 символов, затем символ, который не отображается для других 5 символов и так далее. Но это требует довольно ужасного повторения кода, и нам придется сопоставлять конечный (потенциально неполный) фрагмент отдельно.
Балансировка групп на помощь! Другой способ соответствовать
(.)(?!.{0,5}\1)
это вставить 5 пустых совпадений в стек захвата и попытаться очистить его:
(){5}(.)(?!(?<-1>.)*\2)
*
Позволяет как минимум ноль повторений, точно так же как {0,5}
, и потому , что мы толкнул пять захватов, он не сможет выскочить более чем в 5 раз либо. Это дольше для одного экземпляра этого шаблона, но это гораздо более многократного использования. Поскольку мы выполняем всплывающее окно в отрицательном прогнозе, это не влияет на реальный стек после завершения просмотра. Итак, после просмотра у нас все еще есть 5 элементов в стеке, независимо от того, что произошло внутри. Кроме того, мы можем просто вытолкнуть один элемент из стека перед каждым заголовком и запустить код в цикле, чтобы автоматически уменьшить ширину заголовка с 5 до 0. Таким образом, этот действительно длинный бит на самом деле может быть сокращен до
(){7}((?<-1>)(.)(?!(?<-1>.)*\1\3))*
(Вы можете заметить два отличия: мы нажимаем 7 вместо 5. Один дополнительный захват - это то, что мы выталкиваем перед каждой итерацией, а не после нее. Другой фактически необходим, чтобы мы могли выскочить из стека 7 раз (так как мы хотим цикл, который будет выполняться 7 раз), мы можем исправить эту ошибку в предпросмотре, убедившись, \1
что в стеке остался хотя бы один элемент.)
Прелесть этого в том, что он также может соответствовать завершающему неполному фрагменту, потому что мы никогда не требовали, чтобы он повторялся 7 раз (это просто необходимый максимум, потому что мы не можем чаще выталкивать из стека). Так что все, что нам нужно сделать, это обернуть это в другой цикл и убедиться, что мы достигли конца строки, чтобы получить
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$