mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-11 06:04:27 +08:00
refactor: unified duplicate Anagram classes into a single implementation (#6290)
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
package com.thealgorithms.dynamicprogramming;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Author: Siddhant Swarup Mallick
|
||||
* Github: https://github.com/siddhant2002
|
||||
@ -12,11 +16,6 @@
|
||||
* This program calculates the number of unique paths possible for a robot to reach the bottom-right corner
|
||||
* of an m x n grid using dynamic programming.
|
||||
*/
|
||||
|
||||
package com.thealgorithms.dynamicprogramming;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class UniquePaths {
|
||||
|
||||
private UniquePaths() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
package com.thealgorithms.dynamicprogramming;
|
||||
|
||||
/**
|
||||
*
|
||||
* Author: Janmesh Singh
|
||||
@ -11,9 +13,6 @@
|
||||
* Use DP to return True if the pattern matches the entire text and False otherwise
|
||||
*
|
||||
*/
|
||||
|
||||
package com.thealgorithms.dynamicprogramming;
|
||||
|
||||
public final class WildcardMatching {
|
||||
private WildcardMatching() {
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package com.thealgorithms.maths;
|
||||
|
||||
/**
|
||||
* A number is said to be Dudeney if the sum of the digits, is the cube root of the entered number.
|
||||
* Example- Let the number be 512, its sum of digits is 5+1+2=8. The cube root of 512 is also 8.
|
||||
* Since, the sum of the digits is equal to the cube root of the entered number;
|
||||
* it is a Dudeney Number.
|
||||
*/
|
||||
package com.thealgorithms.maths;
|
||||
|
||||
public final class DudeneyNumber {
|
||||
private DudeneyNumber() {
|
||||
}
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @author Md Asif Joardar
|
||||
*/
|
||||
|
||||
package com.thealgorithms.scheduling;
|
||||
|
||||
import com.thealgorithms.devutils.entities.ProcessDetails;
|
||||
@ -11,6 +7,7 @@ import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* @author Md Asif Joardar
|
||||
* The Round-robin scheduling algorithm is a kind of preemptive First come, First Serve CPU
|
||||
* Scheduling algorithm. This can be understood here -
|
||||
* https://www.scaler.com/topics/round-robin-scheduling-in-os/
|
||||
|
@ -23,7 +23,9 @@ public final class Anagrams {
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach1(String s, String t) {
|
||||
public static boolean areAnagramsBySorting(String s, String t) {
|
||||
s = s.toLowerCase().replaceAll("[^a-z]", "");
|
||||
t = t.toLowerCase().replaceAll("[^a-z]", "");
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
@ -43,17 +45,18 @@ public final class Anagrams {
|
||||
* @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;
|
||||
public static boolean areAnagramsByCountingChars(String s, String t) {
|
||||
s = s.toLowerCase().replaceAll("[^a-z]", "");
|
||||
t = t.toLowerCase().replaceAll("[^a-z]", "");
|
||||
int[] dict = new int[128];
|
||||
for (char ch : s.toCharArray()) {
|
||||
dict[ch]++;
|
||||
}
|
||||
int[] charCount = new int[26];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
charCount[s.charAt(i) - 'a']++;
|
||||
charCount[t.charAt(i) - 'a']--;
|
||||
for (char ch : t.toCharArray()) {
|
||||
dict[ch]--;
|
||||
}
|
||||
for (int count : charCount) {
|
||||
if (count != 0) {
|
||||
for (int e : dict) {
|
||||
if (e != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -70,7 +73,9 @@ public final class Anagrams {
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach3(String s, String t) {
|
||||
public static boolean areAnagramsByCountingCharsSingleArray(String s, String t) {
|
||||
s = s.toLowerCase().replaceAll("[^a-z]", "");
|
||||
t = t.toLowerCase().replaceAll("[^a-z]", "");
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
@ -96,7 +101,9 @@ public final class Anagrams {
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach4(String s, String t) {
|
||||
public static boolean areAnagramsUsingHashMap(String s, String t) {
|
||||
s = s.toLowerCase().replaceAll("[^a-z]", "");
|
||||
t = t.toLowerCase().replaceAll("[^a-z]", "");
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
@ -123,7 +130,9 @@ public final class Anagrams {
|
||||
* @param t the second string
|
||||
* @return true if the strings are anagrams, false otherwise
|
||||
*/
|
||||
public static boolean approach5(String s, String t) {
|
||||
public static boolean areAnagramsBySingleFreqArray(String s, String t) {
|
||||
s = s.toLowerCase().replaceAll("[^a-z]", "");
|
||||
t = t.toLowerCase().replaceAll("[^a-z]", "");
|
||||
if (s.length() != t.length()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
package com.thealgorithms.strings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Two strings are anagrams if they are made of the same letters arranged
|
||||
* differently (ignoring the case).
|
||||
*/
|
||||
public final class CheckAnagrams {
|
||||
private CheckAnagrams() {
|
||||
}
|
||||
/**
|
||||
* Check if two strings are anagrams or not
|
||||
*
|
||||
* @param s1 the first string
|
||||
* @param s2 the second string
|
||||
* @return {@code true} if two string are anagrams, otherwise {@code false}
|
||||
*/
|
||||
public static boolean isAnagrams(String s1, String s2) {
|
||||
int l1 = s1.length();
|
||||
int l2 = s2.length();
|
||||
s1 = s1.toLowerCase();
|
||||
s2 = s2.toLowerCase();
|
||||
Map<Character, Integer> charAppearances = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < l1; i++) {
|
||||
char c = s1.charAt(i);
|
||||
int numOfAppearances = charAppearances.getOrDefault(c, 0);
|
||||
charAppearances.put(c, numOfAppearances + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < l2; i++) {
|
||||
char c = s2.charAt(i);
|
||||
if (!charAppearances.containsKey(c)) {
|
||||
return false;
|
||||
}
|
||||
charAppearances.put(c, charAppearances.get(c) - 1);
|
||||
}
|
||||
|
||||
for (int cnt : charAppearances.values()) {
|
||||
if (cnt != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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.
|
||||
* <p>
|
||||
* 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!");
|
||||
}
|
||||
}
|
||||
}
|
@ -13,36 +13,37 @@ public class AnagramsTest {
|
||||
|
||||
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));
|
||||
new AnagramTestCase("binary", "brainy", true), new AnagramTestCase("adobe", "abode", true), new AnagramTestCase("cat", "act", true), new AnagramTestCase("cat", "cut", false), new AnagramTestCase("Listen", "Silent", true), new AnagramTestCase("Dormitory", "DirtyRoom", true),
|
||||
new AnagramTestCase("Schoolmaster", "TheClassroom", true), new AnagramTestCase("Astronomer", "MoonStarer", true), new AnagramTestCase("Conversation", "VoicesRantOn", true));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach1(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach1(testCase.input1(), testCase.input2()));
|
||||
assertEquals(testCase.expected(), Anagrams.areAnagramsBySorting(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach2(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach2(testCase.input1(), testCase.input2()));
|
||||
assertEquals(testCase.expected(), Anagrams.areAnagramsByCountingChars(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach3(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach3(testCase.input1(), testCase.input2()));
|
||||
assertEquals(testCase.expected(), Anagrams.areAnagramsByCountingCharsSingleArray(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach4(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach4(testCase.input1(), testCase.input2()));
|
||||
assertEquals(testCase.expected(), Anagrams.areAnagramsUsingHashMap(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("anagramTestData")
|
||||
void testApproach5(AnagramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Anagrams.approach5(testCase.input1(), testCase.input2()));
|
||||
assertEquals(testCase.expected(), Anagrams.areAnagramsBySingleFreqArray(testCase.input1(), testCase.input2()));
|
||||
}
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
package com.thealgorithms.strings;
|
||||
|
||||
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 testCheckAnagrams() {
|
||||
String testString1 = "STUDY";
|
||||
String testString2 = "DUSTY";
|
||||
Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckFalseAnagrams() {
|
||||
String testString1 = "STUDY";
|
||||
String testString2 = "random";
|
||||
Assertions.assertFalse(CheckAnagrams.isAnagrams(testString1, testString2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckSameWordAnagrams() {
|
||||
String testString1 = "STUDY";
|
||||
Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckDifferentCasesAnagram() {
|
||||
String testString1 = "STUDY";
|
||||
String testString2 = "dusty";
|
||||
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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user