Skip to main content

Диапазоны и альтернативы. Точная интерпретация «самодельных масок»

Цель: научиться точно читать «маски» из олимпиадных задач, где символы *, ?, |, [] имеют переопределённую автором семантику, и переводить такие маски в стандартные регулярные выражения.


1) Типовые семантики «самодельных масок» (архетипы)

Важно: всегда читайте условие — автор явно задаёт смысл метасимволов. Самые частые варианты:

  • Глоб-подобная (как в shell/Windows):
    • * — любая (в т.ч. пустая) последовательность символов.
    • ? — ровно один любой символ.
    • [abc] — один символ из множества; диапазоны a-z; отрицание часто [!abc] (иногда [^abc]).
    • | обычно НЕ имеет смысла вне групп; если используется — автор специально оговаривает «альтернацию».
  • Маска с ограниченным алфавитом (по условию):
    • * и ? относятся не к «любому символу», а к «любому символу из заданного алфавита» (например, только латиница и цифры).
  • «Литеральная» внутри класса:
    • Внутри [...] символы *, |, !, ? почти всегда трактуются как обычные символы (если автор не переопределил).

Правило безопасности: пока не доказано иное, считайте, что:

  • Снаружи []: */? — подстановки; | — альтернация ТОЛЬКО если это явно написано.
  • Внутри []: мета — это - (диапазон), ] (закрывает), \ (экранирует). Остальное — литералы.

2) Алгоритм перевода маски в регекс

  1. Зафиксируйте алфавит «любой символ» из условия:
    • Любой ASCII: . (или [\x00-\x7F]).
    • Только буквы/цифры: [A-Za-z0-9].
    • Только цифры: [0-9], не \d.
  2. Разберите структуру слева направо:
    • Обычные буквы — как есть (экранируйте спецсимволы регекса).
    • Класс [...] — переносим в регекс-класс, аккуратно с -/]/\.
    • * → соответствующий класс со * (или .*), ? → класс с {1} (или .).
    • Если встречается авторская «альтернация» X|Y — оберните: ^(?:X|Y)$.
  3. Добавьте якоря ^ и $, чтобы проверять всю строку.
  4. Экранируйте спецсимволы регекса вне классов: . ^ $ * + ? ( ) [ ] { } | \
  5. При необходимости уточните «жадность» (обычно не требуется для полных матчей).

Шпаргалка подстановок (часто применимые маппинги):

  • Маска * → Регекс .* или [Алфавит]*
  • Маска ? → Регекс . или [Алфавит]
  • Маска [a-z0-9] → Регекс [a-z0-9] (идентично)
  • Маска [!abc] (если так оговорено) → Регекс [^abc]

3) Важные тонкости классов и диапазонов

  • Диапазоны включительны: [a-g] — 7 символов, [A-S] — 19 символов.
  • Не пишите [A-z] — в регексах это захватывает лишние символы между Z и a. Всегда [A-Za-z].
  • - как литерал: ставьте в начале/конце или экранируйте: [-_+], [A-Z\-].
  • Внутри [] символы |, *, !, ? — литералы (их не нужно экранировать). Экранируйте только -, ], \.

4) Практика: целевая маска SC[a-gA-S][*|!?]OL

Условие (типичное чтение):

  • SC — литералы S и C.
  • [a-gA-S] — один символ из диапазона a..g ИЛИ A..S.
  • [*|!?] — один символ из множества {*, |, !, ?} (внутри [] — все четыре буквальные).
  • OL — литералы O и L.

Перевод в стандартный регекс (ASCII-семантика):

  • Регекс: ^SC[a-gA-S][*|!?]OL$
    Комментарии:
    • Внутри класса [*|!?] экранирование не нужно: | и * — обычные символы.
    • С якорями ^ и $ мы требуем совпадение всей строки.

Проверочные примеры:

  • Совпадает: SCa*OL, SCA!OL, SCg?OL, SCS|OL
  • Не совпадает: SCZ*OL (Z не в [A-S]), SCaOL (нет символа из [*|!?]), xSCa*OL (лишние символы, если проверяем весь матч)

Комбинаторный размер (по полезной привычке считать):

  • [a-g] — 7 символов; [A-S] — 19; итого 26.
  • [*|!?] — 4 символа.
  • Всего строк: 26 × 4 = 104.

5) Ещё три показательных кейса перевода

Кейс 1. Глоб-подобная маска: AB*CD?EF (где * — любая последовательность любых символов, ? — любой символ)

  • Регекс: ^AB.*CD.EF$
  • Если по условию «любой» — только буквы/цифры: ^AB[A-Za-z0-9]*CD[A-Za-z0-9]EF$

Кейс 2. Маска с альтернативами: FOO|BAR? (автор явно сказал: | — «ИЛИ» между подстроками)

  • Регекс: ^(?:FOO|BAR.)$
  • Замечание: если бы | оказался внутри [], он был бы литералом, а не «ИЛИ».

Кейс 3. Отрицательные классы в маске: USER[!0-9][!A-Z]

  • Если автор определил ! как отрицание в классе:
    • Регекс: ^USER[^0-9][^A-Z]$
  • Если нет — ! трактуется как литерал, и маска означает «символ !, 0–9 или A–Z»:
    • Регекс: ^USER[!0-9A-Z]$
      Вывод: без явной оговорки про ! в условии считать его литералом внутри [].

6) Диагностика и отладка

  • Сначала переводите строго и минимально, не меняя семантики.
  • Тестируйте на целевых коротких строках: положительные, отрицательные, граничные.
  • Проверяйте «внутри/снаружи []»: не перепутайте, где | — альтернация, а где — литерал.
  • Якоря ^$ обязательны, иначе совпадёт подстрока, а не вся строка.

7) Мини‑упражнения (с ответами)

  1. Маска: ID[0-9][0-9]-* (где * — любая последовательность букв/цифр/подчёркиваний)

    • Регекс: ^ID[0-9][0-9]-[A-Za-z0-9_]*$
  2. Маска: PASS[AEIOU][a-z]*[!?.] (где * — только строчные латинские; последний — один из !?.)

    • Регекс: ^PASS[AEIOU][a-z]*[!?.]$
  3. Маска: X(Y|Z)[A-F0-3]?T (автор явно определил скобки и | как альтернацию; ? — любой одиночный символ алфавита [A-F0-3])

    • Регекс: ^X(?:Y|Z)[A-F0-3]T$

8) Сводка-памятка

  • Чётко зафиксируйте алфавит для * и ?.
  • Внутри [] | и * — литералы; следите только за -, ], \.
  • Для авторской «альтернации» оборачивайте в (?: … | … ) и якорьте ^…$.
  • Прогоняйте тесты и проверяйте граничные случаи.

Фрагмент корпуса для примеров и тестов

- The quick brown fox jumps over 13 lazy dogs.
- Start_1 middle_text end9
- password PASSword Pa55w0rd GoodPass9
- a____9 A12345 9start1
- name_surname name--surname username_ _username
- abcde9 abcdef ABCDEF1 ABcdefghij1
- HELLOlll IllIlIll safeSAFE2
- abc def abc_def abc-def
- 123456 0000000000 1234567890```