Checking if two Strings are anagram of each other using basic Java

28,946

Solution 1

I would go for something simpler to reason about: two strings are anagrams if, once sorted, they match exactly. So in Java it would be something like:

    String s1 = "cat";
    String s2 = "tac";
    boolean isAnagram = false;
    if (s1.length() == s2.length()) {
        char[] s1AsChar = s1.toCharArray();
        char[] s2AsChar = s2.toCharArray();
        Arrays.sort(s1AsChar);
        Arrays.sort(s2AsChar);
        isAnagram = Arrays.equals(s1AsChar, s2AsChar);
    } 

Solution 2

Here my solution, we count the appearance of each character in the first string then subtracting it from the count in the second string. Finally, check if the character count is not 0 then the two string is not anagram.

public static boolean isAnagram(String a, String b){
    //assume that we are using ASCII
    int[] charCnt = new int[256];
    for(int i = 0; i < a.length(); i++){
        charCnt[a.charAt(i)]++;
    }
    for(int i = 0; i< b.length(); i++){
        charCnt[b.charAt(i)]--;
    }
    for(int i = 0; i<charCnt.length; i++){
        if(charCnt[i] != 0) return false;
    }
    return true;
}

Solution 3

You want to compare the sorted characters. It's a one-liner:

return Arrays.equals(s1.chars().sorted().toArray(),
    s2.chars().sorted().toArray());

Arrays.equals() compares lengths and all elements for you.

Solution 4

Since you seem to be a beginner, heres a solution that doesn´t involve functions from other classes or streams. It does only involve the usage of arrays and the fact that a char can also represent an int.

public static void main(String[] args) throws ParseException {
    String s1= "anagram"; 
    String s2= "margana";  
    // We make use of the fact that a char does also represent an int.
    int lettersS1[] = new int[Character.MAX_VALUE];
    int lettersS2[] = new int[Character.MAX_VALUE];
    if(s1.length()!=s2.length())
       System.out.print("No");
    else {
       // Loop through the String once
       for(int i = 0; i<s1.length() ;++i) {
           // we can just use the char value as an index
           // and increase the value of it. This is our identifier how often 
           // each letter was aviable in the String. Alse case insensitive right now
           lettersS1[s1.toLowerCase().charAt(i)]++;
           lettersS2[s2.toLowerCase().charAt(i)]++;
       }
       // set a flag if the Strings were anagrams
       boolean anag = true;
       // We stop the loop as soon as we noticed they are not anagrams
       for(int i = 0;i<lettersS1.length&&anag;++i) {
           if(lettersS1[i] != lettersS2[i]) {
               // If the values differ they are not anagrams.
               anag = false;
           }
       }
       // Depending on the former loop we know if these two strings are anagrams
       if(anag) {
           System.out.print("Anagram");
       } else {
           System.out.print("No anagram");
       }
    } 
}

Solution 5

One more solution, based on occurrence counter:

static boolean areAnagrams(CharSequence a, CharSequence b) {
    int len = a.length();
    if (len != b.length())
        return false;

    // collect char occurrences in "a"
    Map<Character, Integer> occurrences = new HashMap<>(64);
    for (int i = 0; i < len; i++)
        occurrences.merge(a.charAt(i), 1, Integer::sum);

    // for each char in "b", look for matching occurrence
    for (int i = 0; i < len; i++) {
        char c = b.charAt(i);
        int cc = occurrences.getOrDefault(c, 0);
        if (cc == 0)                        
            return false;            
        occurrences.put(c, cc - 1);
    }
    return true;
}

Though this solution is less elegant than "sort-and-compare", it might be more effective for long strings with a small chances to be anagrams since it operates in O(n) instead of O(n logn) and returns as soon as matching occurrence is not found at some position in a second string.


Stepping out of "Basic Java" territory, I modified the algorithm to handle surrogate pairs as well. Here collected and matched are not chars, but int codepoints:

static boolean areAnagrams(CharSequence a, CharSequence b) {
    int len = a.length();
    if (len != b.length())
        return false;

    // collect codepoint occurrences in "a"
    Map<Integer, Integer> ocr = new HashMap<>(64);
    a.codePoints().forEach(c -> ocr.merge(c, 1, Integer::sum));

    // for each codepoint in "b", look for matching occurrence
    for (int i = 0, c = 0; i < len; i += Character.charCount(c)) {
        int cc = ocr.getOrDefault((c = Character.codePointAt(b, i)), 0);
        if (cc == 0)                        
            return false;            
        ocr.put(c, cc - 1);
    }
    return true;
}
Share:
28,946
Prakhar Londhe
Author by

Prakhar Londhe

Updated on March 25, 2020

Comments

  • Prakhar Londhe
    Prakhar Londhe over 4 years

    I am writing following code in java Netbeans which is working quite good for normal anagrams. But if the two text fields contain words which contain repetitive letters, then the code fail to work. What may be the problem and how can I solve it? I am quite basic to Java and cannot understand Arrays yet.

    String s1= t1.getText(); 
    String s2= t2.getText();  
    int b=0,c=0;
    if(s1.length()!=s2.length())
       System.out.print("No");
    else {
       for(int i=0;i<s1.length();i++) {
          char s = s1.charAt(i);
          for(int j=0;j<s2.length();j++) {
             if(s==s2.charAt(j)){
                b++;
             } 
          }
          if(b==0)
             break;
       }
       if(b==0)
          System.out.print("No");
       else 
          System.out.print("YES");
    } 
    System.out.print(b);
    
  • Alex Salauyou
    Alex Salauyou over 8 years
    I'd add length comparison to fail fast if lengths are different
  • Arnaud
    Arnaud over 8 years
    Wow, it looks like I should have posted my comment as an answer :D .
  • Bohemian
    Bohemian over 8 years
    @sasha no need - Arrays.equals() does that for you
  • Alex Salauyou
    Alex Salauyou over 8 years
    @Bohemian I meant before sorting, to skip it if not necessary
  • RubioRic
    RubioRic over 8 years
    @IvanValeriani You're right. I'm gonna look what exactly is an anagram. My bad english. Deleting my post in 5 ... 4 ...
  • Lillian
    Lillian almost 7 years
    A little improvement to your logic: in second for loop, determine if charCnt[b.charAt(i)] is less than 0
  • Prakhar Londhe
    Prakhar Londhe almost 7 years
    @Antariksh More like both product and sum.. because multiple number can have same sum or product, but not both ;)
  • Antariksh
    Antariksh almost 7 years
    @Prakhar I agree.
  • Lalit Behera
    Lalit Behera about 6 years
    I did it like this:- public boolean isAnagram(String str1, String str2) {return ((str1.replaceAll("\\s","") .toLowerCase() .codePoints() .sorted() .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString() .contentEquals(str2.replaceAll("\\s","").toLowerCase() .codePoints() .sorted() .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString()))); }