mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-12-19 07:00:35 +08:00
Added isogram utility class and unit tests (#6594)
* added isogram utility class and unit tests Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * checking lint issue Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * linter fix Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * fix: HideUtilityClassConstructor Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * linter fix Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * updated isogram class to final Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * pipeline bug fixing Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> * renamed function names and few fixes w.r.t to suggestion Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com> --------- Signed-off-by: JeevaRamanathan <jeevaramanathan.m@infosys.com>
This commit is contained in:
92
src/main/java/com/thealgorithms/strings/Isogram.java
Normal file
92
src/main/java/com/thealgorithms/strings/Isogram.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.thealgorithms.strings;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An isogram (also called heterogram or nonpattern word) is a word in which no
|
||||
* letter of the word occurs more than once. Each character appears exactly
|
||||
* once.
|
||||
*
|
||||
* For example, the word "uncopyrightable" is the longest common English isogram
|
||||
* with 15 unique letters. Other examples include "dermatoglyphics" (15
|
||||
* letters),
|
||||
* "background" (10 letters), "python" (6 letters), and "keyboard" (8 letters).
|
||||
* But words like "hello" and "programming" are not isograms because some
|
||||
* letters
|
||||
* appear multiple times ('l' appears twice in "hello", while 'r', 'm', 'g'
|
||||
* repeat
|
||||
* in "programming").
|
||||
*
|
||||
* Isograms are particularly valuable in creating substitution ciphers and are
|
||||
* studied in recreational linguistics. A perfect pangram, which uses all 26
|
||||
* letters
|
||||
* of the alphabet exactly once, is a special type of isogram.
|
||||
*
|
||||
* Reference from https://en.wikipedia.org/wiki/Heterogram_(literature)#Isograms
|
||||
*/
|
||||
public final class Isogram {
|
||||
/**
|
||||
* Private constructor to prevent instantiation of utility class.
|
||||
*/
|
||||
private Isogram() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is an isogram using boolean array approach.
|
||||
*
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* @param str the input string
|
||||
* @return true if the string is an isogram, false otherwise
|
||||
* @throws IllegalArgumentException if the string contains non-alphabetic
|
||||
* characters
|
||||
*/
|
||||
public static boolean isAlphabeticIsogram(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
str = str.toLowerCase();
|
||||
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char ch = str.charAt(i);
|
||||
if (ch < 'a' || ch > 'z') {
|
||||
throw new IllegalArgumentException("Input contains non-alphabetic character: '" + ch + "'");
|
||||
}
|
||||
}
|
||||
|
||||
boolean[] seenChars = new boolean[26];
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char ch = str.charAt(i);
|
||||
int index = ch - 'a';
|
||||
if (seenChars[index]) {
|
||||
return false;
|
||||
}
|
||||
seenChars[index] = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is an isogram using length comparison approach.
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(k) where k is the number of unique characters
|
||||
*
|
||||
* @param str the input string
|
||||
* @return true if the string is an isogram, false otherwise
|
||||
*/
|
||||
public static boolean isFullIsogram(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
str = str.toLowerCase();
|
||||
|
||||
Set<Character> uniqueChars = new HashSet<>();
|
||||
for (char ch : str.toCharArray()) {
|
||||
uniqueChars.add(ch);
|
||||
}
|
||||
return uniqueChars.size() == str.length();
|
||||
}
|
||||
}
|
||||
117
src/test/java/com/thealgorithms/strings/IsogramTest.java
Normal file
117
src/test/java/com/thealgorithms/strings/IsogramTest.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.thealgorithms.strings;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class IsogramTest {
|
||||
|
||||
record IsogramTestCase(String input, boolean expected) {
|
||||
}
|
||||
|
||||
private static Stream<IsogramTestCase> isAlphabeticIsogram() {
|
||||
return Stream.of(
|
||||
// Valid isograms (only checks letters)
|
||||
new IsogramTestCase("uncopyrightable", true), new IsogramTestCase("dermatoglyphics", true), new IsogramTestCase("background", true), new IsogramTestCase("python", true), new IsogramTestCase("keyboard", true), new IsogramTestCase("clipboard", true), new IsogramTestCase("flowchart", true),
|
||||
new IsogramTestCase("bankruptcy", true), new IsogramTestCase("computer", true), new IsogramTestCase("algorithms", true),
|
||||
|
||||
// Not isograms - letters repeat
|
||||
new IsogramTestCase("hello", false), new IsogramTestCase("programming", false), new IsogramTestCase("java", false), new IsogramTestCase("coffee", false), new IsogramTestCase("book", false), new IsogramTestCase("letter", false), new IsogramTestCase("mississippi", false),
|
||||
new IsogramTestCase("google", false),
|
||||
|
||||
// Edge cases
|
||||
new IsogramTestCase("", true), new IsogramTestCase("a", true), new IsogramTestCase("ab", true), new IsogramTestCase("abc", true), new IsogramTestCase("aa", false), new IsogramTestCase("abcdefghijklmnopqrstuvwxyz", true),
|
||||
|
||||
// Case insensitive
|
||||
new IsogramTestCase("Python", true), new IsogramTestCase("BACKGROUND", true), new IsogramTestCase("Hello", false), new IsogramTestCase("PROGRAMMING", false));
|
||||
}
|
||||
|
||||
private static Stream<IsogramTestCase> isFullIsogram() {
|
||||
return Stream.of(
|
||||
// Valid isograms (checks all characters)
|
||||
new IsogramTestCase("uncopyrightable", true), new IsogramTestCase("dermatoglyphics", true), new IsogramTestCase("background", true), new IsogramTestCase("python", true), new IsogramTestCase("keyboard", true), new IsogramTestCase("clipboard", true), new IsogramTestCase("flowchart", true),
|
||||
new IsogramTestCase("bankruptcy", true), new IsogramTestCase("computer", true), new IsogramTestCase("algorithms", true),
|
||||
|
||||
// Not isograms - characters repeat
|
||||
new IsogramTestCase("hello", false), new IsogramTestCase("programming", false), new IsogramTestCase("java", false), new IsogramTestCase("coffee", false), new IsogramTestCase("book", false), new IsogramTestCase("letter", false), new IsogramTestCase("mississippi", false),
|
||||
new IsogramTestCase("google", false),
|
||||
|
||||
// Edge cases
|
||||
new IsogramTestCase("", true), new IsogramTestCase("a", true), new IsogramTestCase("ab", true), new IsogramTestCase("abc", true), new IsogramTestCase("aa", false), new IsogramTestCase("abcdefghijklmnopqrstuvwxyz", true),
|
||||
|
||||
// Case insensitive
|
||||
new IsogramTestCase("Python", true), new IsogramTestCase("BACKGROUND", true), new IsogramTestCase("Hello", false), new IsogramTestCase("PROGRAMMING", false),
|
||||
|
||||
// Strings with symbols and numbers
|
||||
new IsogramTestCase("abc@def", true), // all characters unique
|
||||
new IsogramTestCase("test-case", false), // 't', 's', 'e' repeat
|
||||
new IsogramTestCase("python123", true), // all characters unique
|
||||
new IsogramTestCase("hello@123", false), // 'l' repeats
|
||||
new IsogramTestCase("abc123!@#", true), // all characters unique
|
||||
new IsogramTestCase("test123test", false), // 't', 'e', 's' repeat
|
||||
new IsogramTestCase("1234567890", true), // all digits unique
|
||||
new IsogramTestCase("12321", false), // '1' and '2' repeat
|
||||
new IsogramTestCase("!@#$%^&*()", true) // all special characters unique
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("isAlphabeticIsogram")
|
||||
void testIsogramByArray(IsogramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Isogram.isAlphabeticIsogram(testCase.input()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("isFullIsogram")
|
||||
void testIsogramByLength(IsogramTestCase testCase) {
|
||||
assertEquals(testCase.expected(), Isogram.isFullIsogram(testCase.input()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullInputByArray() {
|
||||
assertTrue(Isogram.isAlphabeticIsogram(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullInputByLength() {
|
||||
assertTrue(Isogram.isFullIsogram(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyStringByArray() {
|
||||
assertTrue(Isogram.isAlphabeticIsogram(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyStringByLength() {
|
||||
assertTrue(Isogram.isFullIsogram(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAlphabeticIsogramThrowsException() {
|
||||
// Test that IllegalArgumentException is thrown for non-alphabetic characters
|
||||
assertThrows(IllegalArgumentException.class, () -> Isogram.isAlphabeticIsogram("1"));
|
||||
assertThrows(IllegalArgumentException.class, () -> Isogram.isAlphabeticIsogram("@"));
|
||||
assertThrows(IllegalArgumentException.class, () -> Isogram.isAlphabeticIsogram("python!"));
|
||||
assertThrows(IllegalArgumentException.class, () -> Isogram.isAlphabeticIsogram("123algorithm"));
|
||||
assertThrows(IllegalArgumentException.class, () -> Isogram.isAlphabeticIsogram("hello123"));
|
||||
assertThrows(IllegalArgumentException.class, () -> Isogram.isAlphabeticIsogram("!@@#$%^&*()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFullIsogramWithMixedCharacters() {
|
||||
// Test that full isogram method handles all character types without exceptions
|
||||
assertTrue(Isogram.isFullIsogram("abc123"));
|
||||
assertFalse(Isogram.isFullIsogram("test@email")); // 'e' repeats
|
||||
assertFalse(Isogram.isFullIsogram("hello123")); // 'l' repeats
|
||||
assertTrue(Isogram.isFullIsogram("1234567890"));
|
||||
assertFalse(Isogram.isFullIsogram("12321")); // '1' and '2' repeat
|
||||
assertTrue(Isogram.isFullIsogram("!@#$%^&*()"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user