Регулярное решение для этого очень неэффективно .Пожалуйста, рассмотрите этот ответ из чисто академического интереса.
Шаблон, в котором отсутствуют строки, имеющие 4 или более экземпляров одного и того же символа:
^(?!.*(.).*\1.*\1.*\1).*
Последний .*
может быть заменен наболее узкий паттерн, если вам нужно уточнить этот паттерн.
См. демонстрацию regex .
Основная часть здесь - это (?!.*(.).*\1.*\1.*\1)
отрицательный прогноз.Он соответствует любым 0+ символам (если используется Pattern.DOTALL
, любому символу, включая символы новой строки), как можно большему числу, затем он сопоставляется и захватывает (с (.)
) любого символа в Группу 1, а затемсоответствует любым 0+ символам, за которыми следует один и тот же символ 3 раза.Если шаблон найден (сопоставлен), сопоставление всей строки завершится неудачно.
Почему он неэффективен? Шаблон в значительной степени зависит от возврата..*
захватывает все символы до конца строки, затем двигатель возвращается назад, пытаясь разместить некоторый текст для последующих подшаблонов.Вы можете увидеть шаги по возврату сюда .Чем больше .*
, тем более ресурсоемкий шаблон.
Почему ленивый вариант не лучше? ^(?!.*?(.).*?\1.*?\1.*?\1).*
выглядит быстрее с некоторыми строками, иэто будет быстрее, если повторяющиеся символы появляются рядом друг с другом и началом строки.Если они находятся в конце строки, эффективность будет снижаться.Таким образом, если предыдущее регулярное выражение совпадает с 121212
за 77 шагов, то текущее также выполнит то же количество шагов.Однако, если вы проверите его с 1212124444
, вы увидите, что ленивый вариант потерпит неудачу после 139 шагов , а жадный вариант потерпит неудачу после 58 шагов .И наоборот, 4444121212
приведет к сбою отложенного регулярного выражения, 14 шагов против 211 шагов с жадным вариантом .
В Java вы можете использоватьэто
s.matches("(?!.*(.).*\\1.*\\1.*\\1)")
или
s.matches("(?!.*?(.).*?\\1.*?\\1.*?\\1)")
Используйте решение Джейкоба в производстве.