Skip to content

Commit 1af4efa

Browse files
sozelfistvil02
andauthored
Refactor Anagram (TheAlgorithms#825)
* ref: refactor anagram * chore: rename `char_frequency` to `char_count` * tests: add some edge tests * style: rename local variable * docs: remove frequency from doc-str --------- Co-authored-by: Piotr Idzik <65706193+vil02@users.noreply.github.com>
1 parent 858443e commit 1af4efa

File tree

1 file changed

+100
-16
lines changed

1 file changed

+100
-16
lines changed

src/string/anagram.rs

Lines changed: 100 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,111 @@
1-
pub fn check_anagram(s: &str, t: &str) -> bool {
2-
sort_string(s) == sort_string(t)
1+
use std::collections::HashMap;
2+
3+
/// Custom error type representing an invalid character found in the input.
4+
#[derive(Debug, PartialEq)]
5+
pub enum AnagramError {
6+
NonAlphabeticCharacter,
37
}
48

5-
fn sort_string(s: &str) -> Vec<char> {
6-
let mut res: Vec<char> = s.to_ascii_lowercase().chars().collect::<Vec<_>>();
7-
res.sort_unstable();
9+
/// Checks if two strings are anagrams, ignoring spaces and case sensitivity.
10+
///
11+
/// # Arguments
12+
///
13+
/// * `s` - First input string.
14+
/// * `t` - Second input string.
15+
///
16+
/// # Returns
17+
///
18+
/// * `Ok(true)` if the strings are anagrams.
19+
/// * `Ok(false)` if the strings are not anagrams.
20+
/// * `Err(AnagramError)` if either string contains non-alphabetic characters.
21+
pub fn check_anagram(s: &str, t: &str) -> Result<bool, AnagramError> {
22+
let s_cleaned = clean_string(s)?;
23+
let t_cleaned = clean_string(t)?;
24+
25+
Ok(char_count(&s_cleaned) == char_count(&t_cleaned))
26+
}
27+
28+
/// Cleans the input string by removing spaces and converting to lowercase.
29+
/// Returns an error if any non-alphabetic character is found.
30+
///
31+
/// # Arguments
32+
///
33+
/// * `s` - Input string to clean.
34+
///
35+
/// # Returns
36+
///
37+
/// * `Ok(String)` containing the cleaned string (no spaces, lowercase).
38+
/// * `Err(AnagramError)` if the string contains non-alphabetic characters.
39+
fn clean_string(s: &str) -> Result<String, AnagramError> {
40+
s.chars()
41+
.filter(|c| !c.is_whitespace())
42+
.map(|c| {
43+
if c.is_alphabetic() {
44+
Ok(c.to_ascii_lowercase())
45+
} else {
46+
Err(AnagramError::NonAlphabeticCharacter)
47+
}
48+
})
49+
.collect()
50+
}
51+
52+
/// Computes the histogram of characters in a string.
53+
///
54+
/// # Arguments
55+
///
56+
/// * `s` - Input string.
57+
///
58+
/// # Returns
59+
///
60+
/// * A `HashMap` where the keys are characters and values are their count.
61+
fn char_count(s: &str) -> HashMap<char, usize> {
62+
let mut res = HashMap::new();
63+
for c in s.chars() {
64+
*res.entry(c).or_insert(0) += 1;
65+
}
866
res
967
}
1068

1169
#[cfg(test)]
1270
mod tests {
1371
use super::*;
1472

15-
#[test]
16-
fn test_check_anagram() {
17-
assert!(check_anagram("", ""));
18-
assert!(check_anagram("A", "a"));
19-
assert!(check_anagram("anagram", "nagaram"));
20-
assert!(check_anagram("abcde", "edcba"));
21-
assert!(check_anagram("sIlEnT", "LiStEn"));
22-
23-
assert!(!check_anagram("", "z"));
24-
assert!(!check_anagram("a", "z"));
25-
assert!(!check_anagram("rat", "car"));
73+
macro_rules! test_cases {
74+
($($name:ident: $test_case:expr,)*) => {
75+
$(
76+
#[test]
77+
fn $name() {
78+
let (s, t, expected) = $test_case;
79+
assert_eq!(check_anagram(s, t), expected);
80+
assert_eq!(check_anagram(t, s), expected);
81+
}
82+
)*
83+
}
84+
}
85+
86+
test_cases! {
87+
empty_strings: ("", "", Ok(true)),
88+
empty_and_non_empty: ("", "Ted Morgan", Ok(false)),
89+
single_char_same: ("z", "Z", Ok(true)),
90+
single_char_diff: ("g", "h", Ok(false)),
91+
valid_anagram_lowercase: ("cheater", "teacher", Ok(true)),
92+
valid_anagram_with_spaces: ("madam curie", "radium came", Ok(true)),
93+
valid_anagram_mixed_cases: ("Satan", "Santa", Ok(true)),
94+
valid_anagram_with_spaces_and_mixed_cases: ("Anna Madrigal", "A man and a girl", Ok(true)),
95+
new_york_times: ("New York Times", "monkeys write", Ok(true)),
96+
church_of_scientology: ("Church of Scientology", "rich chosen goofy cult", Ok(true)),
97+
mcdonalds_restaurants: ("McDonald's restaurants", "Uncle Sam's standard rot", Err(AnagramError::NonAlphabeticCharacter)),
98+
coronavirus: ("coronavirus", "carnivorous", Ok(true)),
99+
synonym_evil: ("evil", "vile", Ok(true)),
100+
synonym_gentleman: ("a gentleman", "elegant man", Ok(true)),
101+
antigram: ("restful", "fluster", Ok(true)),
102+
sentences: ("William Shakespeare", "I am a weakish speller", Ok(true)),
103+
part_of_speech_adj_to_verb: ("silent", "listen", Ok(true)),
104+
anagrammatized: ("Anagrams", "Ars magna", Ok(true)),
105+
non_anagram: ("rat", "car", Ok(false)),
106+
invalid_anagram_with_special_char: ("hello!", "world", Err(AnagramError::NonAlphabeticCharacter)),
107+
invalid_anagram_with_numeric_chars: ("test123", "321test", Err(AnagramError::NonAlphabeticCharacter)),
108+
invalid_anagram_with_symbols: ("check@anagram", "check@nagaram", Err(AnagramError::NonAlphabeticCharacter)),
109+
non_anagram_length_mismatch: ("abc", "abcd", Ok(false)),
26110
}
27111
}

0 commit comments

Comments
 (0)