7

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);
BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
Prakhar Londhe
  • 1,431
  • 1
  • 12
  • 26
  • 8
    Note that you could get two char arrays from the Strings with `toCharArray()` , sort them with `Arrays.sort`, and ensure that both arrays have exactly the same content. – Arnaud Mar 23 '16 at 14:31
  • I saw this thing somewhere but I am not at all familiar with Arrays. Isn't there any other method which can be used? If there isn't, I should better learn the Arrays. – Prakhar Londhe Mar 23 '16 at 14:32
  • 1
    @prakharlondhe you should learn array no matter what. They are a very common data structure in coding. – nerdlyist Mar 23 '16 at 14:35
  • @prakharlondhe If you are looking for a solution without using array, you can update you question. – user3437460 Mar 23 '16 at 14:56

5 Answers5

28

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);
    } 
Ivan Valeriani
  • 620
  • 5
  • 13
  • 2
    I'd add length comparison to fail fast if lengths are different – Alex Salauyou Mar 23 '16 at 14:50
  • 3
    Wow, it looks like I should have posted my comment as an answer :D . – Arnaud Mar 23 '16 at 14:53
  • @sasha no need - Arrays.equals() does that for you – Bohemian Mar 23 '16 at 15:01
  • 1
    @Bohemian I meant before sorting, to skip it if not necessary – Alex Salauyou Mar 23 '16 at 15:03
  • @IvanValeriani You're right. I'm gonna look what exactly is an anagram. My bad english. Deleting my post in 5 ... 4 ... – RubioRic Mar 23 '16 at 15:07
  • @Antariksh More like both product and sum.. because multiple number can have same sum or product, but not both ;) – Prakhar Londhe Aug 25 '17 at 18:31
  • @Prakhar I agree. – Antariksh Aug 28 '17 at 07:12
  • 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()))); } – Lalit Behera May 24 '18 at 11:07
9

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.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
9

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;
}
Long Vu
  • 131
  • 1
  • 5
  • 1
    A little improvement to your logic: in second for loop, determine if charCnt[b.charAt(i)] is less than 0 – Lillian Jul 06 '17 at 03:19
5

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");
       }
    } 
}
SomeJavaGuy
  • 7,307
  • 2
  • 21
  • 33
3

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;
}
Community
  • 1
  • 1
Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67