Необходимо проверить, являются ли две строки анаграммами друг друга

Анаграмма — это литературный приём, при котором буквы или звуки определённого слова (или словосочетания) переставляются, в результате чего получается другое слово или словосочетание. Такой метод ещё называют «перебуква».

Напишите функцию, проверяющую, являются ли две переданные в функцию строки анаграммами друг друга (регистр букв не имеет значения). Важны только символы, пробелы или знаки препинания не учитываются:

function isAnagram (str, str2) {
    // ...
}

isAnagram('finder', 'Friend'); // true
isAnagram('hello', 'bye'); // false

Теория: как проверяются анаграммы

Анаграммы — это две строки, которые состоят из одних и тех же значимых символов в одинаковых количествах, но, возможно, в разном порядке.
По условию регистр не важен, а пробелы и знаки препинания не учитываются, поэтому перед сравнением требуется привести строки к единому виду и удалить “неважные” символы.

Шаг 1 — нормализация и приведение регистра

В Unicode один и тот же “видимый” символ иногда может быть записан разными последовательностями кодовых точек (например, буква с диакритикой может быть “составной” или “комбинированной”).
Чтобы сравнение было корректным, полезно приводить строки к одной форме нормализации, например normalize("NFC"), чтобы одинаково выглядящие символы имели одинаковое внутреннее представление.

Далее требуется игнорировать регистр:

  • Обычно достаточно toLowerCase(), так как цель — сделать единый регистр без зависимости от локали.
  • toLocaleLowerCase() меняет регистр с учётом правил конкретной локали, из‑за чего в некоторых языках возможны отличия.
Если требуется максимально предсказуемое поведение независимо от окружения, обычно выбирается toLowerCase(), а не локалезависимые преобразования.

Шаг 2 — удаление пробелов и пунктуации

Самая частая ошибка — удалять только пробелы, забывая про табы, переносы строк, дефисы, запятые и другие символы.
По условию важны только символы (с практической точки зрения удобнее считать важными “буквы и цифры”), поэтому остальные знаки (пробелы, пунктуация) должны игнорироваться.

Надёжный подход для разных алфавитов:

  • Оставлять только буквы и цифры с помощью регулярного выражения с Unicode-свойствами: /[\p{L}\p{N}]/gu.
  • Флаг u обязателен для корректной работы Unicode-режима.
  • Флаг g нужен, чтобы извлекались все совпадения.
Запись \p{...} относится к современным возможностям JavaScript (Unicode property escapes) и работает в актуальных версиях браузеров и Node.js; в устаревших окружениях может потребоваться иной фильтр (например, ограничиться латиницей/кириллицей вручную).

Шаг 3 — сравнение: сортировка или частоты

Есть два стандартных пути:

  1. Сортировка:
  • Очистить строки.
  • Преобразовать в массив символов, отсортировать и сравнить результат.
  • Плюс: простота.
  • Минус: сложность O(n log n) из-за сортировки.
  1. Частоты (часто считается базовым “алгоритмическим” решением):
  • Посчитать, сколько раз встречается каждый символ в первой строке.
  • Для второй строки вычитать эти количества.
  • Если при вычитании получилось отрицательное значение — анаграммы невозможны.
  • После обработки все счётчики должны стать нулевыми.
  • Сложность O(n).

Небольшая схема процесса (универсальная для обоих подходов):

Входные строки
   │
   ├─ normalize("NFC")
   ├─ toLowerCase()
   ├─ оставить только буквы/цифры (игнорировать пробелы/пунктуацию)
   │
   └─ сравнение:
       ├─ вариант А: сортировка и сравнение строк
       └─ вариант Б: частоты символов и проверка нуля

Таблица: выбор подхода

ПодходИдеяВремяПамятьКогда выбирать
СортировкаОтсортировать символы и сравнитьO(n log n)O(n)Учебные примеры, небольшие строки
Частоты (Map)Сравнить количества каждого символаO(n)O(k)Длинные строки, важна эффективность
Частоты + ранний выходВычитать и завершать при ошибкеO(n) (в среднем)O(k)Частые “не анаграмма”, нужна скорость

Примечания:

  • n — длина строки после очистки.
  • k — число различных символов после очистки.

Кратко: для проверки анаграмм необходимо очистить строки (нормализация Unicode, приведение к нижнему регистру, игнорирование пробелов и пунктуации), после чего сравнить либо отсортированные наборы символов, либо (предпочтительнее) частоты символов через Map, что даёт линейную сложность по длине очищенной строки.