diff --git a/src/main/java/com/thealgorithms/strings/CheckAnagrams.java b/src/main/java/com/thealgorithms/strings/CheckAnagrams.java index 87ec3fae..a20e8b4a 100644 --- a/src/main/java/com/thealgorithms/strings/CheckAnagrams.java +++ b/src/main/java/com/thealgorithms/strings/CheckAnagrams.java @@ -8,13 +8,6 @@ import java.util.Map; * differently (ignoring the case). */ public class CheckAnagrams { - - public static void main(String[] args) { - assert isAnagrams("Silent", "Listen"); - assert isAnagrams("This is a string", "Is this a string"); - assert !isAnagrams("There", "Their"); - } - /** * Check if two strings are anagrams or not * @@ -50,4 +43,66 @@ public class CheckAnagrams { } return true; } + + /** + * If given strings contain Unicode symbols. + * The first 128 ASCII codes are identical to Unicode. + * This algorithm is case-sensitive. + * + * @param s1 the first string + * @param s2 the second string + * @return true if two string are anagrams, otherwise false + */ + public static boolean isAnagramsUnicode(String s1, String s2) { + int[] dict = new int[128]; + for (char ch : s1.toCharArray()) { + dict[ch]++; + } + for (char ch : s2.toCharArray()) { + dict[ch]--; + } + for (int e : dict) { + if (e != 0) { + return false; + } + } + return true; + } + + /** + * If given strings contain only lowercase English letters. + *
+ * The main "trick": + * To map each character from the first string 's1' we need to subtract an integer value of 'a' character + * as 'dict' array starts with 'a' character. + * + * @param s1 the first string + * @param s2 the second string + * @return true if two string are anagrams, otherwise false + */ + public static boolean isAnagramsOptimised(String s1, String s2) { + // 26 - English alphabet length + int[] dict = new int[26]; + for (char ch : s1.toCharArray()) { + checkLetter(ch); + dict[ch - 'a']++; + } + for (char ch : s2.toCharArray()) { + checkLetter(ch); + dict[ch - 'a']--; + } + for (int e : dict) { + if (e != 0) { + return false; + } + } + return true; + } + + private static void checkLetter(char ch) { + int index = ch - 'a'; + if (index < 0 || index >= 26) { + throw new IllegalArgumentException("Strings must contain only lowercase English letters!"); + } + } } diff --git a/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java b/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java index 4e285433..82a75a13 100644 --- a/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java +++ b/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java @@ -1,35 +1,69 @@ package com.thealgorithms.strings; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class CheckAnagramsTest { + private static final String MESSAGE = "Strings must contain only lowercase English letters!"; + // CHECK METHOD isAnagrams() @Test - public void CheckAnagrams() { + public void testCheckAnagrams() { String testString1 = "STUDY"; String testString2 = "DUSTY"; - assertTrue(CheckAnagrams.isAnagrams(testString1, testString2)); + Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString2)); } @Test - public void CheckFalseAnagrams() { + public void testCheckFalseAnagrams() { String testString1 = "STUDY"; String testString2 = "random"; - assertFalse(CheckAnagrams.isAnagrams(testString1, testString2)); + Assertions.assertFalse(CheckAnagrams.isAnagrams(testString1, testString2)); } @Test - public void CheckSameWordAnagrams() { + public void testCheckSameWordAnagrams() { String testString1 = "STUDY"; - assertTrue(CheckAnagrams.isAnagrams(testString1, testString1)); + Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString1)); } @Test - public void CheckDifferentCasesAnagram() { + public void testCheckDifferentCasesAnagram() { String testString1 = "STUDY"; String testString2 = "dusty"; - assertTrue(CheckAnagrams.isAnagrams(testString1, testString2)); + Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString2)); + } + + // CHECK METHOD isAnagramsUnicode() + // Below tests work with strings which consist of Unicode symbols & the algorithm is case-sensitive. + @Test + public void testStringAreValidAnagramsCaseSensitive() { + Assertions.assertTrue(CheckAnagrams.isAnagramsUnicode("Silent", "liSten")); + Assertions.assertTrue(CheckAnagrams.isAnagramsUnicode("This is a string", "is This a string")); + } + + @Test + public void testStringAreNotAnagramsCaseSensitive() { + Assertions.assertFalse(CheckAnagrams.isAnagramsUnicode("Silent", "Listen")); + Assertions.assertFalse(CheckAnagrams.isAnagramsUnicode("This is a string", "Is this a string")); + } + + // CHECK METHOD isAnagramsOptimised() + // Below tests work with strings which consist of only lowercase English letters + @Test + public void testOptimisedAlgorithmStringsAreValidAnagrams() { + Assertions.assertTrue(CheckAnagrams.isAnagramsOptimised("silent", "listen")); + Assertions.assertTrue(CheckAnagrams.isAnagramsOptimised("mam", "amm")); + } + + @Test + public void testOptimisedAlgorithmShouldThrowExceptionWhenStringsContainUppercaseLetters() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> CheckAnagrams.isAnagramsOptimised("Silent", "Listen")); + Assertions.assertEquals(exception.getMessage(), MESSAGE); + + exception = assertThrows(IllegalArgumentException.class, () -> Assertions.assertFalse(CheckAnagrams.isAnagramsOptimised("This is a string", "Is this a string"))); + Assertions.assertEquals(exception.getMessage(), MESSAGE); } }