Улучшения анаграммы

Только что закончил недавнее домашнее задание, но я знаю, что оно может быть более эффективным. Он читает два слова из командной строки, игнорирует пробелы и знаки препинания и определяет, являются ли они анаграммами. Что у меня есть ниже; насколько я могу судить, он полностью функционален.

/**
 * Find out if a string is an anagram of another string
 * 
 */

import java.util.Arrays;

public class Anagram
{
    public static void main(String[] args)
    {
        if (args.length != 2)
            System.out.println("You did not enter two words!");        
        else            
            printWords(args[0], args[1]);                              
    }

    // method to determine whether two strings have the same chars
    public static boolean areAnagrams(String wordOne, String wordTwo) 
    {
        // new strings for letters only
        String ltrsOnlyOne = lettersOnly(wordOne);
        String ltrsOnlyTwo = lettersOnly(wordTwo);      

        // convert strings to lowercase char arrays
        char[] first = ltrsOnlyOne.toLowerCase().toCharArray();
        char[] second = ltrsOnlyTwo.toLowerCase().toCharArray();

        // sort char arrays using sort method
        Arrays.sort(first);
        Arrays.sort(second);

        if (Arrays.equals(first, second))
            return true;
        else
            return false;
    }

    public static String lettersOnly(String word) 
    {
        int length = word.length();
        StringBuilder end = new StringBuilder(length);
        char x;

        for (int i = (length - 1); i >= 0; i--) {
            x = word.charAt(i);
            if (Character.isLetter(x)) {
                end.append(x);
            }
        }
        return end.toString();
    }

    public static void printWords(String wordOne, String wordTwo)
    {
       boolean b = areAnagrams(wordOne, wordTwo);
       if (b == true) {
            System.out.println(wordOne + " is an anagram of "+ wordTwo);
       }

       if (b == false) {
            System.out.println(wordOne + " is not an anagram of "+ wordTwo);
       }
    }
}

6 ответов

Решение

Исправлена ​​ошибка: lettersOnly возвращаемое значение теряется

Ваш lettersOnly Метод не изменяет свои аргументы на месте, он возвращает новые строки. Когда вы вызываете его, вам нужно что-то сделать с этим возвращаемым значением. В противном случае вы просто вызываете его и выбрасываете результат.

// method to determine whether two strings have the same chars
public static boolean sameChars(String wordOne, String wordTwo) 
{
    lettersOnly(wordOne);
    lettersOnly(wordTwo);
    wordOne = lettersOnly(wordOne);
    wordTwo = lettersOnly(wordTwo);

    ...
}

Удаление мертвого кода

В main() ты звонишь sameChars без проверки его возвращаемого значения. поскольку sameChars не изменяет свои аргументы на месте и не имеет других побочных эффектов, этот вызов является мертвым кодом и должен быть удален.

public static void main(String[] args)
{
    if (args.length != 2) {
        System.out.println("You did not enter two words!");
    }

    sameChars(args[0], args[1]);        
    printWords(args[0], args[1]);             
}

Рефакторинг if заявления

В то время как ваш if утверждения правильны, используя == сравнивать с true а также false не идиоматично. Нет необходимости в явных сравнениях, когда вы используете логические значения. Это короче и читается лучше, чтобы просто пропустить сравнения.

if (sameChars(wordOne, wordTwo) == true) {
if (sameChars(wordOne, wordTwo)) {
    System.out.println(wordOne + " is an anagram of "+ wordTwo);
}
if (sameChars(wordOne, wordTwo) == false) { if (!sameChars(wordOne, wordTwo)) { System.out.println(wordOne + " is not an anagram of "+ wordTwo); }

Обратите внимание, как == false заменяется эквивалентным ! (НЕ).

На самом деле нет причин повторять звонок sameChars, Наличие кода, который выглядит так похоже, должен вызвать небольшую тревогу в вашем уме. "Должен быть способ устранить дублирующийся код", - думаете вы. Ах да, давайте переключимся второй if для else!

if (sameChars(wordOne, wordTwo)) {
    System.out.println(wordOne + " is an anagram of "+ wordTwo);
}
else {
    System.out.println(wordOne + " is not an anagram of "+ wordTwo);
}

продвинутый

Аналогичным образом вы можете упростить следующий набор утверждений:

if (Arrays.equals(first, second))
    return true;
else
    return false;

Когда результат equals() верно, вы возвращаете истину. Иначе, когда оно ложно, вы возвращаете ложь. Если вы думаете об этом, вы фактически просто возвращаете все equals вернулся. Вы могли бы на самом деле устранить if а также else полностью и просто вернуть результат equals непосредственно. Ха!

return Arrays.equals(first, second);

Это изменение немного более продвинутое, чем предыдущее. Вы можете найти это изменение немного странным, если вы никогда не видели его раньше. Но это действительно эквивалентно четырехстрочной версии.

Обратный цикл

Обычно, когда программисты пишут for циклы они начинаются с 0 и повторяются до некоторой верхней границы. Есть ли причина, по которой вы написали свой цикл как нисходящий цикл?

for (int i = (length - 1); i >= 0; i--) {
    x = word.charAt(i);
    if (Character.isLetter(x)) {
        end.append(x);
    }
}

Некоторые программисты зацикливаются в обратном направлении в сомнительной попытке быть "более эффективным". Как и в, они только выполняют расчет (length - 1) один раз, так что это немного быстрее. Лично я считаю это пустой тратой времени и умственных способностей.

На самом деле, написано задом наперед lettersOnly Функция имеет странный побочный эффект - возвращает строки в обратном порядке после удаления ненужных символов. Оказывается, это не имеет значения, поскольку вы позже сортируете символы в алфавитном порядке. Но это тот тип вещей, который может укусить тебя позже. Так уж вышло, что с сортировкой вам сошло с рук. Я бы переключил цикл на итерацию в обычном порядке от 0 до n:

for (int i = 0; i < length; ++i) {
    x = word.charAt(i);
    if (Character.isLetter(x)) {
        end.append(x);
    }
}
  • Время выполнения: если вы хотите улучшить время выполнения, вы можете посчитать, сколько раз каждая буква встречается в каждом слове, а затем сравнить количество. Таким образом, вы можете избежать сортировки (которая имеет O(n * log n) сложность) и решить задачу линейно (O(n)Время

  • Javadoc: незначительный момент, комментарий Javadoc для класса (/** ... */) должен прийти непосредственно перед объявлением класса (public class ...). Прямо сейчас у вас есть оператор import между комментарием javadoc и объявлением класса.

Я бы сдвинул шаг санации lettersOnly() быть отдельным методом снаружи sameChars()позвонил с главного. Это не более эффективно, но это более чистый код:

public static void main(String[] args) 
{ 
    if (args.length != 2) { 
        System.out.println("You did not enter two words!"); 
    } 

    String wordOne = lettersOnly(args[0]); 
    String wordTwo = lettersOnly(args[1]);    

    printWords(wordOne, wordTwo);              
} 

// method to determine whether two strings have the same chars 
public static boolean sameChars(String wordOne, String wordTwo)  
{ 
    // convert strings to lowercase char arrays 
    char[] first = wordOne.toLowerCase().toCharArray(); 
    char[] second = wordTwo.toLowerCase().toCharArray(); 

    Arrays.sort(first); 
    Arrays.sort(second); 

    return  Arrays.equals(first, second);
} 

public static void printWords(String wordOne, String wordTwo) 
{ 
    boolean isAnagram = sameChars(wordOne, wordTwo);         

   if (isAnagram) 
   { 
        System.out.println(wordOne + " is an anagram of "+ wordTwo); 
   } 
   else
   { 
        System.out.println(wordOne + " is not an anagram of "+ wordTwo); 
   } 
} 

Сегодня я сделал этот код, который не использует массивы, чтобы проверить, является ли одно слово анаграммой другого... эффективно работает для меня... Надеюсь, это поможет вам.

import java.util.Scanner;

public class Anagrams
{
//method for if lengths are different
public static boolean length (String word, String check)
{
    if (word.length() !=check.length())
    {
        return false;
    }
    return true;
}

// display information
public static void main (String[]args)
{
    Scanner input = new Scanner (System.in);
    //prompt user to enter two words and assign one to String "word" and the other to String "check"
    System.out.println("Please enter a word:");
    String word = input.nextLine();
    System. out. println("Please enter another word to check if it is an anagram of the first:");
    String check = input.nextLine();

    int add = 0;//keeps track of number of letters that are correct to eachother
    //first check if length is correct
    if (length(word,check) == false)
    {
        System.out.println("There is no anagram present.");
    }
    else if (length(word,check)== true) //if length is correct, continue checking for anagram
    {
       // check if each letter is in both words
        for (int i=0;i<word.length();i++) //for loop through each letter in word 
       {
           for (int x=0;x<check.length();x++) //for loop through each letter in check
           {
               if (word.charAt(i)==check.charAt(x)) //if letter in first word is the same as one in the next
               {
                   add++;
                   break;// break out of loop section if the letter is found more than once
               }
           }
       }
       if (add == word.length())//if the number of matching letters equals the number of letters in 
       //the word entered by the user, display that the words are anagrams of eachother
       {
           System. out.println (check + " is an anagram of " + word);
       }
    }
}

При удалении не-букв зачем зачем вставлять результат обратно в строку, когда все, что ты собираешься сделать, это снова поместить их в массив?

char[] first = word1.toCharArray();
int numLettersFirst = 0;
for(char l: first)
  if(Character.isLetter(l))
      first[numLettersFirst++] = Character.toLower(l);

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

Вам нужно только позвонить sameChars один раз. В противном случае, в ложном случае, вы строчными буквами и итерации по вашим массивам дважды. Пытаться:

public static void printWords(String wordOne, String wordTwo)
 {
   boolean same = sameChars(wordOne, wordTwo);
   if (same) {
        System.out.println(wordOne + " is an anagram of "+ wordTwo);
    } else {
        System.out.println(wordOne + " is not an anagram of "+ wordTwo);
    }
}
Другие вопросы по тегам