refactor: Anagrams
(#5390)
This commit is contained in:
parent
7e9cdad3ee
commit
93e417544d
@ -10,141 +10,130 @@ import java.util.HashMap;
|
||||
* also the word binary into brainy and the word adobe into abode.
|
||||
* Reference from https://en.wikipedia.org/wiki/Anagram
|
||||
*/
|
||||
public class Anagrams {
|
||||
public final class Anagrams {
|
||||
private Anagrams() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 4 approaches are provided for anagram checking. approach 2 and approach 3 are similar but
|
||||
* differ in running time.
|
||||
* OUTPUT :
|
||||
* first string ="deal" second string ="lead"
|
||||
* Output: Anagram
|
||||
* Input and output is constant for all four approaches
|
||||
* 1st approach Time Complexity : O(n logn)
|
||||
* Auxiliary Space Complexity : O(1)
|
||||
* 2nd approach Time Complexity : O(n)
|
||||
* Auxiliary Space Complexity : O(1)
|
||||
* 3rd approach Time Complexity : O(n)
|
||||
* Auxiliary Space Complexity : O(1)
|
||||
* 4th approach Time Complexity : O(n)
|
||||
* Auxiliary Space Complexity : O(n)
|
||||
* 5th approach Time Complexity: O(n)
|
||||
* Auxiliary Space Complexity: O(1)
|
||||
* Checks if two strings are anagrams by sorting the characters and comparing them.
|
||||
* Time Complexity: O(n log n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* @param s the first string
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
String first = "deal";
|
||||
String second = "lead";
|
||||
// All the below methods takes input but doesn't return any output to the main method.
|
||||
Anagrams nm = new Anagrams();
|
||||
System.out.println(nm.approach2(first, second)); /* To activate methods for different approaches*/
|
||||
System.out.println(nm.approach1(first, second)); /* To activate methods for different approaches*/
|
||||
System.out.println(nm.approach3(first, second)); /* To activate methods for different approaches*/
|
||||
System.out.println(nm.approach4(first, second)); /* To activate methods for different approaches*/
|
||||
}
|
||||
|
||||
boolean approach1(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
} else {
|
||||
char[] c = s.toCharArray();
|
||||
char[] d = t.toCharArray();
|
||||
Arrays.sort(c);
|
||||
Arrays.sort(d); /* In this approach the strings are stored in the character arrays and
|
||||
both the arrays are sorted. After that both the arrays are compared
|
||||
for checking anangram */
|
||||
|
||||
return Arrays.equals(c, d);
|
||||
}
|
||||
}
|
||||
|
||||
boolean approach2(String a, String b) {
|
||||
if (a.length() != b.length()) {
|
||||
return false;
|
||||
} else {
|
||||
int[] m = new int[26];
|
||||
int[] n = new int[26];
|
||||
for (char c : a.toCharArray()) {
|
||||
m[c - 'a']++;
|
||||
}
|
||||
// In this approach the frequency of both the strings are stored and after that the
|
||||
// frequencies are iterated from 0 to 26(from 'a' to 'z' ). If the frequencies match
|
||||
// then anagram message is displayed in the form of boolean format Running time and
|
||||
// space complexity of this algo is less as compared to others
|
||||
for (char c : b.toCharArray()) {
|
||||
n[c - 'a']++;
|
||||
}
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (m[i] != n[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean approach3(String s, String t) {
|
||||
public static boolean approach1(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
// this is similar to approach number 2 but here the string is not converted to character
|
||||
// array
|
||||
else {
|
||||
int[] a = new int[26];
|
||||
int[] b = new int[26];
|
||||
int k = s.length();
|
||||
for (int i = 0; i < k; i++) {
|
||||
a[s.charAt(i) - 'a']++;
|
||||
b[t.charAt(i) - 'a']++;
|
||||
}
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (a[i] != b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
char[] c = s.toCharArray();
|
||||
char[] d = t.toCharArray();
|
||||
Arrays.sort(c);
|
||||
Arrays.sort(d);
|
||||
return Arrays.equals(c, d);
|
||||
}
|
||||
|
||||
boolean approach4(String s, String t) {
|
||||
/**
|
||||
* Checks if two strings are anagrams by counting the frequency of each character.
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* @param s the first string
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach2(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
// This approach is done using hashmap where frequencies are stored and checked iteratively
|
||||
// and if all the frequencies of first string match with the second string then anagram
|
||||
// message is displayed in boolean format
|
||||
else {
|
||||
HashMap<Character, Integer> nm = new HashMap<>();
|
||||
HashMap<Character, Integer> kk = new HashMap<>();
|
||||
for (char c : s.toCharArray()) {
|
||||
nm.put(c, nm.getOrDefault(c, 0) + 1);
|
||||
}
|
||||
for (char c : t.toCharArray()) {
|
||||
kk.put(c, kk.getOrDefault(c, 0) + 1);
|
||||
}
|
||||
// It checks for equal frequencies by comparing key-value pairs of two hashmaps
|
||||
return nm.equals(kk);
|
||||
}
|
||||
}
|
||||
|
||||
boolean approach5(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
// Approach is different from above 4 aproaches.
|
||||
// Here we initialize an array of size 26 where each element corresponds to the frequency of
|
||||
// a character.
|
||||
int[] freq = new int[26];
|
||||
// iterate through both strings, incrementing the frequency of each character in the first
|
||||
// string and decrementing the frequency of each character in the second string.
|
||||
int[] charCount = new int[26];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
int pos1 = s.charAt(i) - 'a';
|
||||
int pos2 = s.charAt(i) - 'a';
|
||||
freq[pos1]++;
|
||||
freq[pos2]--;
|
||||
charCount[s.charAt(i) - 'a']++;
|
||||
charCount[t.charAt(i) - 'a']--;
|
||||
}
|
||||
// iterate through the frequency array and check if all the elements are zero, if so return
|
||||
// true else false
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (freq[i] != 0) {
|
||||
for (int count : charCount) {
|
||||
if (count != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two strings are anagrams by counting the frequency of each character
|
||||
* using a single array.
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* @param s the first string
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach3(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
int[] charCount = new int[26];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
charCount[s.charAt(i) - 'a']++;
|
||||
charCount[t.charAt(i) - 'a']--;
|
||||
}
|
||||
for (int count : charCount) {
|
||||
if (count != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two strings are anagrams using a HashMap to store character frequencies.
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* @param s the first string
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach4(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
HashMap<Character, Integer> charCountMap = new HashMap<>();
|
||||
for (char c : s.toCharArray()) {
|
||||
charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1);
|
||||
}
|
||||
for (char c : t.toCharArray()) {
|
||||
if (!charCountMap.containsKey(c) || charCountMap.get(c) == 0) {
|
||||
return false;
|
||||
}
|
||||
charCountMap.put(c, charCountMap.get(c) - 1);
|
||||
}
|
||||
return charCountMap.values().stream().allMatch(count -> count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two strings are anagrams using an array to track character frequencies.
|
||||
* This approach optimizes space complexity by using only one array.
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* @param s the first string
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach5(String s, String t) {
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
int[] freq = new int[26];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
freq[s.charAt(i) - 'a']++;
|
||||
freq[t.charAt(i) - 'a']--;
|
||||
}
|
||||
for (int count : freq) {
|
||||
if (count != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,48 @@
|
||||
package com.thealgorithms.strings;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class AnagramsTest {
|
||||
|
||||
@Test
|
||||
public void isAlphabetical() {
|
||||
String input1 = "late";
|
||||
Anagrams anagrams = new Anagrams();
|
||||
assertTrue(anagrams.approach1(input1, "tale"));
|
||||
assertTrue(anagrams.approach1(input1, "teal"));
|
||||
assertTrue(anagrams.approach2(input1, "tale"));
|
||||
assertTrue(anagrams.approach2(input1, "teal"));
|
||||
assertTrue(anagrams.approach3(input1, "tale"));
|
||||
assertTrue(anagrams.approach3(input1, "teal"));
|
||||
assertTrue(anagrams.approach4(input1, "tale"));
|
||||
assertTrue(anagrams.approach4(input1, "teal"));
|
||||
assertTrue(anagrams.approach5(input1, "teal"));
|
||||
record AnagramTestCase(String input1, String input2, boolean expected) {
|
||||
}
|
||||
|
||||
private static Stream<AnagramTestCase> anagramTestData() {
|
||||
return Stream.of(new AnagramTestCase("late", "tale", true), new AnagramTestCase("late", "teal", true), new AnagramTestCase("listen", "silent", true), new AnagramTestCase("hello", "olelh", true), new AnagramTestCase("hello", "world", false), new AnagramTestCase("deal", "lead", true),
|
||||
new AnagramTestCase("binary", "brainy", true), new AnagramTestCase("adobe", "abode", true), new AnagramTestCase("cat", "act", true), new AnagramTestCase("cat", "cut", false));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach1(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach1(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach2(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach2(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach3(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach3(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach4(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach4(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach5(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach5(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user