1

I have written a Java program to find Anagram for 2 strings.

For Reference: Two strings are anagrams if they are written using the same exact letters, ignoring space, punctuation and capitalization. Each letter should have the same count in both strings. For example, Army and Mary are anagram of each other.

Program:

package practice;

import java.util.ArrayList;
import java.util.List;

public class Anagram_String {

    public static void main(String[] args) {

        String s1="mary";
        String s2="army";
        int k=0;
        List<String> matchedChar= new ArrayList<String>();
        String charmatch="";

        char[] ch1= s1.toLowerCase().toCharArray();
        char[] ch2= s2.toLowerCase().toCharArray();

        if(s1.length()==s2.length())
        {

            for(int i=0;i<s1.length();i++)
            {
                for(int j=0;j<s2.length();j++)
                {
                    if(ch1[i]==ch2[j])
                    {
                        k++;
                        charmatch=String.valueOf(ch1[i]);
                        System.out.println(charmatch);
                        matchedChar.add(charmatch);
                        System.out.println("Arraylist value is "+matchedChar.toString());
                        System.out.println(matchedChar.size());
                    }
                }

                k=0;
            }

            String arrayValue=matchedChar.toString();
            System.out.println("Array value is "+arrayValue);

            if(arrayValue.contains(s2)){

                System.out.println("String 1 and String 2 are anagrams of each other");

            }
            else
            {
                System.out.println("String 1 and String 2 are not anagrams of each other");
            }

        }

    }

}

Output:

m
Arraylist value is [m]    
1  
a  
Arraylist value is [m, a]    
2   
r   
Arraylist value is [m, a, r]    
3  
y   
Arraylist value is [m, a, r, y]   
4   
Array value is [m, a, r, y]  
String 1 and String 2 are not anagrams of each other

Here if you see all the characters are added to to the arraylist but when compared with the string, it is showing the output as they are not anagrams of each other.

Kindly help me to find solution for this.

Thank you,

Henrik Aasted Sørensen
  • 6,172
  • 9
  • 46
  • 56
user2987894
  • 33
  • 1
  • 1
  • 8
  • 1
    How about using regex to remove all non-text characters from your String, then sorting your char arrays and comparing for equality. – Hovercraft Full Of Eels Nov 25 '16 at 12:36
  • In addition to the solution provided by lock_this I would like to point a few things in your current code that make it fail. You are rebuilding exactly string 1 and then try to compare it with string 2. That won't work of course. Then you are comparing the toString of a List with a string which means that you compare literaly [m, a,r, y] with army – François Dupire Nov 25 '16 at 12:51
  • See [this solution using prime numbers](http://stackoverflow.com/a/28948975/4391450). This might failed using long String, need to test the limits off this. – AxelH Nov 25 '16 at 13:43

8 Answers8

4

What I think is that your solution will work only for words with unique characters, and time complexity will be O(n^2) (where n - is the length of String).

However, there is a better solution for such problem:

  1. Take String.toCharArray() value for each string
  2. Sort those arrays
  3. If those arrays are equal, then your words are anagrams
nazlo
  • 446
  • 3
  • 9
  • Before doing all that he might also want to remove spaces and punctuation from both Strings as he doesn't want to consider them for the comparisons. Use of regex like Hovercraft suggested in his comment would be fine to do that. – François Dupire Nov 25 '16 at 12:49
  • It is possible to solve the problem in O(n) as explained in my answer. Ordering the arrays needs at least O(n log(n)) – Davide Lorenzo MARINO Nov 25 '16 at 12:55
  • @lock_this additionally this not solve the problem of ignoring space, punctualization and non letters characters – Davide Lorenzo MARINO Nov 25 '16 at 12:59
  • @user2987894 this is not a correct solution for your question. This solution doesn't work for the following part "ignoring space, punctuation and capitalization" – Davide Lorenzo MARINO Nov 25 '16 at 14:24
2

You can count number of letters in both strings. If both strings have the same number of letters they are anagrams.

You can use an int[] to store number of letters.

public static boolean anagrams(String a, String b) {
    int[] letters = new int[26];

    // Convert to upper case because the test is case insensitive
    a = a.toUpperCase();  
    b = b.toUpperCase();
    for (int i = 0; i < a.length(); i++) {
        char ch = a.charAt(i);
        if (ch < 'A' || ch > 'Z') {
            continue; // Skip non letters
        }
        letters[ch - 'A']++;   // Increment number of the current letter
    }
    for (int i = 0; i < b.length(); i++) {
        char ch = b.charAt(i);
        if (ch < 'A' || ch > 'Z') {
            continue; // Skip non letters
        }
        letters[ch - 'A']--;   // Decrement number of the current letter

    }
    for (int i = 0; i < letters.length; i++) {
        if (letters[i] != 0) {
            // If there are more or less of this letter 
            // in the first string it is not an anagram
            return false;
        }
    }
    return true;
}

Note this algorithm is done in O(n) where n is the number of letters of each string. Sorting the strings needs at least O(n log(n))


Taking the idea from AxelH's comments it is possible to create an external method to loop as follow.

private void countLetters(int[] letters, String str, int incrementFactor) {
    for (int i = 0; i < str.length(); i++) {
        char ch = str.charAt(i);
        if (ch < 'A' || ch > 'Z') {
            continue; // Skip non letters
        }
        letters[ch - 'A'] += incrementFactor; 
    }
}

public static boolean anagrams(String a, String b) {
    int[] letters = new int[26];

    countLetters(letters, a.toUpperCase(), 1);   // Note the +1
    countLetters(letters, b.toUpperCase(), -1);  // Note the -1

    for (int i = 0; i < letters.length; i++) {
        if (letters[i] != 0) {
            // If there are more or less of this letter 
            // in the first string it is not an anagram
            return false;
        }
    }
    return true;
}

This seems to me a more readable and elegant way. Thanks AxelH.


Note In the previous code there are expressions like letters[ch - 'A']++. This line of code use an interesting properties of type char of java that is a special primitive numeric type, so it is possible to use mathematical operations on it.

In particular:

'A' - 'A' --> 0
'B' - 'A' --> 1
'C' - 'A' --> 2
'D' - 'A' --> 3
...
'Z' - 'A' --> 25

So this expression can be used to give an index to a letter starting from 0 for A ending to 25 for Z.

Davide Lorenzo MARINO
  • 22,769
  • 4
  • 33
  • 48
  • You could do both array in the same loop, at least until the `min(a.length, b.length)` then finish the longer array. – AxelH Nov 25 '16 at 13:01
  • Yes it is possible also to combine first two arrays. I add also your idea. Thx – Davide Lorenzo MARINO Nov 25 '16 at 13:02
  • Yes, that prevent to write two loops with only a increment/decrement different ;) – AxelH Nov 25 '16 at 13:04
  • @AxelH no sorry is not possible to combine two arrays because you can have a different number of letters in the two strings. For example "Mary" and "ARMY!!!!" are anagrams by definition of the questionnaire – Davide Lorenzo MARINO Nov 25 '16 at 13:04
  • Can I edit your question with my code ? (Adding at the end ;) ) – AxelH Nov 25 '16 at 13:08
  • @AxelH as I said you can't use one loop because there can be a different number of letters in string a and string b. – Davide Lorenzo MARINO Nov 25 '16 at 13:17
  • **For readability**, you can simply concat the Strings and loop on the result, keeping the initial length off the first String to know when you need to increment or decrement. With this, you have one loop (doing the same number of iteration) but with less line. With more lines, you need to loop first on the minimum length two compare the beginning of the String then loop on the rest. I will write an answer to show you this. – AxelH Nov 25 '16 at 13:20
  • This not add readability. Eventually is better to refactor the code and call an exteral loop twice, with a parameter to increment or decrement. But concatenating the two strings and adding a test inside the loop is not very elegant and readable. In any case I add also your idea modified as I said. – Davide Lorenzo MARINO Nov 25 '16 at 13:23
  • Using a simple ternary condition to get the increment is done on one line, so this still readable. Yes this is one condition on each iteration (only problem I see). I stop here the chat ;) – AxelH Nov 25 '16 at 13:31
  • Is simpler to said: "add 1 for each letter in the first string, substract 1 for each letter in the second string, if at the end there are more or less lecters is not an anagram" or "concatenate the two strings, add 1 for each letter of the concatenated string until the length of the first original string, then substract one for the remaing characters, f at the end there are more or less lecters is not an anagram" ? Readable doesn't means less lines of code, but more understandable – Davide Lorenzo MARINO Nov 25 '16 at 13:34
  • Can anyone say what will be count captured by the line "letters[ch - 'A']++; " – user2987894 Nov 25 '16 at 14:52
1

Your outputs says it itself: Array value is [m, a, r, y]

As mentioned above I would also just create array and sort them, but here is the solution you may be searching for:

        String s1="mary";
        String s2="army";
        List<String> matchedChar= new ArrayList<String>();
        String charmatch="";

        char[] ch1= s1.toLowerCase().toCharArray();
        char[] ch2= s2.toLowerCase().toCharArray();

        if(s1.length()==s2.length())
        {

            for(int i=0;i<s1.length();i++)
            {
                for(int j=0;j<s2.length();j++)
                {
                    if(ch1[i]==ch2[j])
                    {
                        charmatch=String.valueOf(ch1[i]);
                        System.out.println(charmatch);
                        matchedChar.add(charmatch);
                        System.out.println("Arraylist value is "+matchedChar.toString());
                        System.out.println(matchedChar.size());
                    }
                }
            }

            String arrayValue="";
            for (String s : matchedChar){
                arrayValue = arrayValue + s;
            }
            System.out.println("Array value is "+arrayValue);
            System.out.println("s1 value is "+s1);

            if(arrayValue.equals(s1)){

                System.out.println("String 1 and String 2 are anagrams of each other");

            }
            else
            {
                System.out.println("String 1 and String 2 are not anagrams of each other");
            }

        }
Tchopane
  • 161
  • 8
1

My answer is quite similar to Marine's, but takes a little higher-level approach with Java 8 streams, making the code a little more concise and readable:

public class Application {

public boolean isAnagramsEqual(String str1, String str2) {
    Map<Character, Long> count = countChars(str1);
    Map<Character, Long> count2 = countChars(str2);

    return count.equals(count2);
}

private Map<Character, Long> countChars(String str) {
    return str.toLowerCase()                 
            .chars().mapToObj(ch -> (char) ch)      //convert into stream of Characters
            .filter(Character::isLetterOrDigit)     //filter out not-needed chars
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 
}}

Method countChars creates a map with each unique character mapped to it's count in the given string. It may be a little less performant than Marine's, but it's still O(n).

Shem
  • 520
  • 2
  • 10
0

This is how you can do it by sorting the arrays:

public static void main(String[] args) {
    System.out.println(isAnagram("mary", "army"));
}

public static boolean isAnagram(String s1, String s2){
    char[] c1 = s1.toLowerCase().toCharArray();
    char[] c2 = s2.toLowerCase().toCharArray();
    Arrays.sort(c1);
    Arrays.sort(c2);

    boolean anagram = false;
    if(Arrays.equals(c1, c2)){anagram = true;}

    return anagram;
}
Alex Karlsson
  • 256
  • 1
  • 11
  • Sorting the arrays needs a O(n log(n)) while it is possible to solve it in O(n) – Davide Lorenzo MARINO Nov 25 '16 at 12:56
  • Thats true, but if the difference in nano seconds isnt a problem when doing this type of simple task, my solution is a lot easier to understand and write. – Alex Karlsson Nov 25 '16 at 13:06
  • Thank you the suggestion Alex and Lorenzo. I learned a lot from your solutions – user2987894 Nov 25 '16 at 13:25
  • @AlexKarlsson not only the algorithm is not O(n), but it doesn't work for anagrams like "Mary" and "ARMY?!?" – Davide Lorenzo MARINO Nov 25 '16 at 13:29
  • Where in the description does it say that question marks and such should be taken in to consideration? Thats like creating a method that gives the sum of two integers. And then mentioning that you didnt think about the fact that "tk1f" + "k2rt" doesnt equal 3? But i agree about the lower upper case part, didnt add that. Edited my answer. – Alex Karlsson Nov 25 '16 at 13:51
0

Use .split("(?!^)") on your String to make it an String Array. Next sort arrays and compare it. It's the best option for me too.

SURU
  • 148
  • 1
  • 1
  • 10
0

In this code i converted my string into char array using the code :String.toCharArray() inside a function named toSort() and sorted the words in the string. Then inside Isanagram() method I checked whether it is anagram or not. For that first I have to make sure whether the sorted strings are of same length or not after I compared each character in one string with other. Here is the full code try to decompose each method and study.

   import java.util.Scanner;

public class StANaEx1 {



String toSort(String s5){
    int i,j;
    char temp;
    char ch1[] = s5.toCharArray();
    int len = ch1.length;
    for(i=0;i<len;i++){
        for(j=i+1;j<len;j++){
            if(ch1[i]>ch1[j]){
                temp = ch1[i];
                ch1[i] = ch1[j];
                ch1[j] = temp;
            }
        }
    }
    String s6 = new String(ch1);
return s6;
}



void isAnagram(String s9,String s10){
    int i,len1,len2,flag=0;
    System.out.println("s9 : "+s9);
    System.out.println("s10 : "+s10);
    System.out.println("");
    s9 = s9.trim();                     //To remove white spaces again no need.I used because my compiler didn't recognize my replace() method in main() method. 
    s10 = s10.trim();
    len1 = s9.length();
    len2 = s10.length();
    System.out.println("len1 : "+len1);     //To check the length of the given strings without white spaces.
    System.out.println("len2 : "+len2);
    System.out.println("");
    for(i=0;i<len1;i++){
        System.out.println("s9["+i+"] : "+s9.charAt(i));        //Error checking.
    }
    System.out.println("");
    for(i=0;i<len2;i++){
        System.out.println("s10["+i+"] : "+s10.charAt(i));
    }
    System.out.println("");
    if(len1!=len2){
        System.out.println("Not Anagram string length different");
    }
    else{
        for(i=0;i<len1;i++){                    //Since string lengths are same len1 = len2.
            if(s9.charAt(i)!=s10.charAt(i)){
                flag=1;
                break;
            }
        }
        if(flag==0){
            System.out.println("Anagram");
        }
        else{
            System.out.println("Not Anagram");
        }
    }
}




public static void main(String args[]){
    Scanner sc = new Scanner(System.in);
    StANaEx1 ob1 = new StANaEx1();
    System.out.println("-----Anagram checking-----");
    System.out.println("");
    System.out.println("Enter the 1st String: ");
    System.out.println("");
    String s1 = sc.nextLine();
    s1 = s1.replace("//s", "");         //This is to remove white spaces.
    String s3 = s1.toLowerCase();       //Change the input string to lower case in order to make sorting easy.
    System.out.println("");
    System.out.println("Enter the next String: ");
    String s2 = sc.nextLine();
    s2 = s2.replace("//s", "");
    String s4 = s2.toLowerCase();
    System.out.println("");

    String s7 = ob1.toSort(s3);
    String s8 = ob1.toSort(s4);

    ob1.isAnagram(s7,s8);

    sc.close();

}
}

Output

   -----Anagram checking-----

Enter the 1st String: 

Mary

Enter the next String: 
army

s9 : amry
s10 : amry

len1 : 4
len2 : 4

s9[0] : a
s9[1] : m
s9[2] : r
s9[3] : y

s10[0] : a
s10[1] : m
s10[2] : r
s10[3] : y

Anagram

Output 2

-----Anagram checking-----

Enter the 1st String: 

Sniper

Enter the next String: 
kill

s9 : einprs
s10 : ikll

len1 : 6
len2 : 4

s9[0] : e
s9[1] : i
s9[2] : n
s9[3] : p
s9[4] : r
s9[5] : s

s10[0] : i
s10[1] : k
s10[2] : l
s10[3] : l

Not Anagram string length different
Arun HC
  • 3
  • 1
  • 5
0
import java.util.Arrays;

public class AnagramString {

    public static void main(String[] args) {

        String str1="Keep";
        String str2="peek";

        //convert the string into the array with lower casing its character 

        char arrstr1[]=str1.toLowerCase().toCharArray();  
        char arrstr2[]=str2.toLowerCase().toCharArray(); 

        //sort the array of character by acending order

        Arrays.sort(arrstr1);
        Arrays.sort(arrstr2);

        //set true boolean value to the status

        boolean status=true;

        //comparing the char array

         status = Arrays.equals(arrstr1, arrstr2);//here we get true value if they are containing the same character

        System.out.println(Arrays.toString(arrstr1));
        System.out.println(Arrays.toString(arrstr2));

        if(arrstr1.length==arrstr2.length  && status) {
            System.out.println("2 string are anagram");
        }else {
            System.out.println("2 string are not anagram");
        }

    }

}
David Buck
  • 3,439
  • 29
  • 24
  • 31
  • Please don't post only code as an answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually of higher quality, and are more likely to attract upvotes – David Buck Apr 22 '20 at 11:09