Skip to content

Commit 2c019ea

Browse files
author
alxkm
committed
refactor: Anagrams
1 parent 101cb95 commit 2c019ea

File tree

2 files changed

+99
-117
lines changed

2 files changed

+99
-117
lines changed

src/main/java/com/thealgorithms/strings/Anagrams.java

Lines changed: 88 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -13,138 +13,125 @@
1313
public class Anagrams {
1414

1515
/**
16-
* 4 approaches are provided for anagram checking. approach 2 and approach 3 are similar but
17-
* differ in running time.
18-
* OUTPUT :
19-
* first string ="deal" second string ="lead"
20-
* Output: Anagram
21-
* Input and output is constant for all four approaches
22-
* 1st approach Time Complexity : O(n logn)
23-
* Auxiliary Space Complexity : O(1)
24-
* 2nd approach Time Complexity : O(n)
25-
* Auxiliary Space Complexity : O(1)
26-
* 3rd approach Time Complexity : O(n)
27-
* Auxiliary Space Complexity : O(1)
28-
* 4th approach Time Complexity : O(n)
29-
* Auxiliary Space Complexity : O(n)
30-
* 5th approach Time Complexity: O(n)
31-
* Auxiliary Space Complexity: O(1)
16+
* Checks if two strings are anagrams by sorting the characters and comparing them.
17+
* Time Complexity: O(n log n)
18+
* Space Complexity: O(n)
19+
*
20+
* @param s the first string
21+
* @param t the second string
22+
* @return true if the strings are anagrams, false otherwise
3223
*/
33-
public static void main(String[] args) {
34-
String first = "deal";
35-
String second = "lead";
36-
// All the below methods takes input but doesn't return any output to the main method.
37-
Anagrams nm = new Anagrams();
38-
System.out.println(nm.approach2(first, second)); /* To activate methods for different approaches*/
39-
System.out.println(nm.approach1(first, second)); /* To activate methods for different approaches*/
40-
System.out.println(nm.approach3(first, second)); /* To activate methods for different approaches*/
41-
System.out.println(nm.approach4(first, second)); /* To activate methods for different approaches*/
42-
}
43-
44-
boolean approach1(String s, String t) {
24+
public static boolean approach1(String s, String t) {
4525
if (s.length() != t.length()) {
4626
return false;
47-
} else {
48-
char[] c = s.toCharArray();
49-
char[] d = t.toCharArray();
50-
Arrays.sort(c);
51-
Arrays.sort(d); /* In this approach the strings are stored in the character arrays and
52-
both the arrays are sorted. After that both the arrays are compared
53-
for checking anangram */
54-
55-
return Arrays.equals(c, d);
5627
}
28+
char[] c = s.toCharArray();
29+
char[] d = t.toCharArray();
30+
Arrays.sort(c);
31+
Arrays.sort(d);
32+
return Arrays.equals(c, d);
5733
}
5834

59-
boolean approach2(String a, String b) {
60-
if (a.length() != b.length()) {
35+
/**
36+
* Checks if two strings are anagrams by counting the frequency of each character.
37+
* Time Complexity: O(n)
38+
* Space Complexity: O(1)
39+
*
40+
* @param s the first string
41+
* @param t the second string
42+
* @return true if the strings are anagrams, false otherwise
43+
*/
44+
public static boolean approach2(String s, String t) {
45+
if (s.length() != t.length()) {
6146
return false;
62-
} else {
63-
int[] m = new int[26];
64-
int[] n = new int[26];
65-
for (char c : a.toCharArray()) {
66-
m[c - 'a']++;
67-
}
68-
// In this approach the frequency of both the strings are stored and after that the
69-
// frequencies are iterated from 0 to 26(from 'a' to 'z' ). If the frequencies match
70-
// then anagram message is displayed in the form of boolean format Running time and
71-
// space complexity of this algo is less as compared to others
72-
for (char c : b.toCharArray()) {
73-
n[c - 'a']++;
74-
}
75-
for (int i = 0; i < 26; i++) {
76-
if (m[i] != n[i]) {
77-
return false;
78-
}
47+
}
48+
int[] charCount = new int[26];
49+
for (int i = 0; i < s.length(); i++) {
50+
charCount[s.charAt(i) - 'a']++;
51+
charCount[t.charAt(i) - 'a']--;
52+
}
53+
for (int count : charCount) {
54+
if (count != 0) {
55+
return false;
7956
}
80-
return true;
8157
}
58+
return true;
8259
}
8360

84-
boolean approach3(String s, String t) {
61+
/**
62+
* Checks if two strings are anagrams by counting the frequency of each character
63+
* using a single array.
64+
* Time Complexity: O(n)
65+
* Space Complexity: O(1)
66+
*
67+
* @param s the first string
68+
* @param t the second string
69+
* @return true if the strings are anagrams, false otherwise
70+
*/
71+
public static boolean approach3(String s, String t) {
8572
if (s.length() != t.length()) {
8673
return false;
8774
}
88-
// this is similar to approach number 2 but here the string is not converted to character
89-
// array
90-
else {
91-
int[] a = new int[26];
92-
int[] b = new int[26];
93-
int k = s.length();
94-
for (int i = 0; i < k; i++) {
95-
a[s.charAt(i) - 'a']++;
96-
b[t.charAt(i) - 'a']++;
97-
}
98-
for (int i = 0; i < 26; i++) {
99-
if (a[i] != b[i]) {
100-
return false;
101-
}
75+
int[] charCount = new int[26];
76+
for (int i = 0; i < s.length(); i++) {
77+
charCount[s.charAt(i) - 'a']++;
78+
charCount[t.charAt(i) - 'a']--;
79+
}
80+
for (int count : charCount) {
81+
if (count != 0) {
82+
return false;
10283
}
103-
return true;
10484
}
85+
return true;
10586
}
10687

107-
boolean approach4(String s, String t) {
88+
/**
89+
* Checks if two strings are anagrams using a HashMap to store character frequencies.
90+
* Time Complexity: O(n)
91+
* Space Complexity: O(n)
92+
*
93+
* @param s the first string
94+
* @param t the second string
95+
* @return true if the strings are anagrams, false otherwise
96+
*/
97+
public static boolean approach4(String s, String t) {
10898
if (s.length() != t.length()) {
10999
return false;
110100
}
111-
// This approach is done using hashmap where frequencies are stored and checked iteratively
112-
// and if all the frequencies of first string match with the second string then anagram
113-
// message is displayed in boolean format
114-
else {
115-
HashMap<Character, Integer> nm = new HashMap<>();
116-
HashMap<Character, Integer> kk = new HashMap<>();
117-
for (char c : s.toCharArray()) {
118-
nm.put(c, nm.getOrDefault(c, 0) + 1);
119-
}
120-
for (char c : t.toCharArray()) {
121-
kk.put(c, kk.getOrDefault(c, 0) + 1);
101+
HashMap<Character, Integer> charCountMap = new HashMap<>();
102+
for (char c : s.toCharArray()) {
103+
charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1);
104+
}
105+
for (char c : t.toCharArray()) {
106+
if (!charCountMap.containsKey(c) || charCountMap.get(c) == 0) {
107+
return false;
122108
}
123-
// It checks for equal frequencies by comparing key-value pairs of two hashmaps
124-
return nm.equals(kk);
109+
charCountMap.put(c, charCountMap.get(c) - 1);
125110
}
111+
return charCountMap.values().stream().allMatch(count -> count == 0);
126112
}
127113

128-
boolean approach5(String s, String t) {
114+
/**
115+
* Checks if two strings are anagrams using an array to track character frequencies.
116+
* This approach optimizes space complexity by using only one array.
117+
* Time Complexity: O(n)
118+
* Space Complexity: O(1)
119+
*
120+
* @param s the first string
121+
* @param t the second string
122+
* @return true if the strings are anagrams, false otherwise
123+
*/
124+
public static boolean approach5(String s, String t) {
129125
if (s.length() != t.length()) {
130126
return false;
131127
}
132-
// Approach is different from above 4 aproaches.
133-
// Here we initialize an array of size 26 where each element corresponds to the frequency of
134-
// a character.
135128
int[] freq = new int[26];
136-
// iterate through both strings, incrementing the frequency of each character in the first
137-
// string and decrementing the frequency of each character in the second string.
138129
for (int i = 0; i < s.length(); i++) {
139-
int pos1 = s.charAt(i) - 'a';
140-
int pos2 = s.charAt(i) - 'a';
141-
freq[pos1]++;
142-
freq[pos2]--;
130+
freq[s.charAt(i) - 'a']++;
131+
freq[t.charAt(i) - 'a']--;
143132
}
144-
// iterate through the frequency array and check if all the elements are zero, if so return
145-
// true else false
146-
for (int i = 0; i < 26; i++) {
147-
if (freq[i] != 0) {
133+
for (int count : freq) {
134+
if (count != 0) {
148135
return false;
149136
}
150137
}
Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
package com.thealgorithms.strings;
22

3-
import static org.junit.jupiter.api.Assertions.assertTrue;
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
44

5-
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.CsvSource;
67

78
public class AnagramsTest {
8-
9-
@Test
10-
public void isAlphabetical() {
11-
String input1 = "late";
12-
Anagrams anagrams = new Anagrams();
13-
assertTrue(anagrams.approach1(input1, "tale"));
14-
assertTrue(anagrams.approach1(input1, "teal"));
15-
assertTrue(anagrams.approach2(input1, "tale"));
16-
assertTrue(anagrams.approach2(input1, "teal"));
17-
assertTrue(anagrams.approach3(input1, "tale"));
18-
assertTrue(anagrams.approach3(input1, "teal"));
19-
assertTrue(anagrams.approach4(input1, "tale"));
20-
assertTrue(anagrams.approach4(input1, "teal"));
21-
assertTrue(anagrams.approach5(input1, "teal"));
9+
@ParameterizedTest
10+
@CsvSource({"late, tale, true", "late, teal, true", "listen, silent, true", "hello, olelh, true", "hello, world, false", "deal, lead, true", "binary, brainy, true", "adobe, abode, true", "cat, act, true", "cat, cut, false"})
11+
void testApproach1(String input1, String input2, boolean expected) {
12+
assertEquals(Anagrams.approach1(input1, input2), expected);
13+
assertEquals(Anagrams.approach2(input1, input2), expected);
14+
assertEquals(Anagrams.approach3(input1, input2), expected);
15+
assertEquals(Anagrams.approach4(input1, input2), expected);
16+
assertEquals(Anagrams.approach5(input1, input2), expected);
2217
}
2318
}

0 commit comments

Comments
 (0)