На этой неделе мы проверим, являются ли два слова или фразы анаграммами. Возвращает true, если они есть, и false, если нет. Следуйте вместе с Leetcode # 242. Мы решим эту проблему с помощью 2 решений.

Слова, являющиеся анаграммами, имеют те же буквы, но переставлены в другом порядке. Например, распространенная анаграмма «слушай» и «молчи». Иногда это могут быть такие фразы, как «Джим Моррисон» и «Мр. Mojo Risin» или «анаграмма» и «пилить барана», как указано выше.

В первом решении мы удалим все знаки препинания, используя регулярное выражение и .replace, а затем преобразуем каждую строку во все строчные буквы для более точного сравнения. Затем мы разделим каждую строку, чтобы она стала массивом, отсортируем массив и объединимся, чтобы снова стать строкой. Мы не можем вызвать sort для строки напрямую, потому что sort — это метод массива.

На этом этапе они должны выглядеть одинаково, если являются анаграммами, поэтому мы можем проверить их на равенство и вернуть true или false с помощью условного тернарного оператора.

Используя в качестве примера «listen» и «silent», finalStringA и finalStringB будут выглядеть как «eilnst».

function isAnagram(stringA, stringB) {
  let lowerCaseA = stringA.replace(/[^\w]/g, "").toLowerCase()
  let lowerCaseB = stringB.replace(/[^\w]/g, "").toLowerCase()
  let finalStringA = lowerCaseA.split('').sort().join('')
  let finalStringB = lowerCaseB.split('').sort().join('')
  return finalStringA === finalStringB ? true : false
}

Для нашего второго решения мы начнем с замены символов, отличных от слов, и преобразования всего в нижний регистр, как мы делали раньше. Мы также создаем счетчики частоты для каждой строки, с ключом в виде буквы и значением в виде количества раз, которое буква появляется. Пока наш код будет выглядеть так:

function isAnagram(stringA, stringB){
  let a = stringA.replace(/[^\w]/g, "").toLowerCase();
  let b = stringB.replace(/[^\w]/g, "").toLowerCase();
  let objA = {};
  let objB = {};
  for (let char of a) {
    objA[char] = objA[char] + 1 || 1;
  }
  for (let char of b) {
    objB[char] = objB[char] + 1 || 1;
  }
}

Далее мы проверим длину ключей каждого объекта. Если они не одинаковой длины, мы знаем, что слова не могут быть анаграммами, и можем вернуть false.

Последнее, что мы проверим, — это фактические символы в каждом частотомере. Если они не совпадают, мы знаем, что можем немедленно вернуть false.

Если мы пройдем через проверку длины и символов, это будет означать, что слова должны быть анаграммами. Поэтому мы можем вернуть true.

Вот наш окончательный результат:

function isAnagram(stringA, stringB){
  // remove non-word characters and convert to lower case
  let a = stringA.replace(/[^\w]/g, "").toLowerCase();
  let b = stringB.replace(/[^\w]/g, "").toLowerCase();
  // create frequency counters
  let objA = {};
  let objB = {};
  for (let char of a) {
    objA[char] = objA[char] + 1 || 1;
  }
  for (let char of b) {
     objB[char] = objB[char] + 1 || 1;
  }
  // check to see if object length matches
  if (Object.keys(objA).length !== Object.keys(objB).length){
     return false;
  }
  
  // iterate through object to see if char doesn't match
  for (let char in objA) {
     if (objA[char] !== objB[char]) {
        return false;
     }
  }
  // must be an anagram!
  return true;
}