Checking if two Strings are anagram of each other using basic Java
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 char
s, 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;
}
Prakhar Londhe
Updated on March 25, 2020Comments
-
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 over 8 yearsI'd add length comparison to fail fast if lengths are different
-
Arnaud over 8 yearsWow, it looks like I should have posted my comment as an answer :D .
-
Bohemian over 8 years@sasha no need - Arrays.equals() does that for you
-
Alex Salauyou over 8 years@Bohemian I meant before sorting, to skip it if not necessary
-
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 almost 7 yearsA little improvement to your logic: in second for loop, determine if charCnt[b.charAt(i)] is less than 0
-
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 almost 7 years@Prakhar I agree.
-
Lalit Behera about 6 yearsI 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()))); }