Skip to content

Commit 4aaa25e

Browse files
authored
Merge branch 'master' into ref/string/z_algorithm
2 parents 99aba0f + 596697c commit 4aaa25e

File tree

2 files changed

+141
-95
lines changed

2 files changed

+141
-95
lines changed

src/data_structures/trie.rs

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
1+
//! This module provides a generic implementation of a Trie (prefix tree).
2+
//! A Trie is a tree-like data structure that is commonly used to store sequences of keys
3+
//! (such as strings, integers, or other iterable types) where each node represents one element
4+
//! of the key, and values can be associated with full sequences.
5+
16
use std::collections::HashMap;
27
use std::hash::Hash;
38

9+
/// A single node in the Trie structure, representing a key and an optional value.
410
#[derive(Debug, Default)]
511
struct Node<Key: Default, Type: Default> {
12+
/// A map of children nodes where each key maps to another `Node`.
613
children: HashMap<Key, Node<Key, Type>>,
14+
/// The value associated with this node, if any.
715
value: Option<Type>,
816
}
917

18+
/// A generic Trie (prefix tree) data structure that allows insertion and lookup
19+
/// based on a sequence of keys.
1020
#[derive(Debug, Default)]
1121
pub struct Trie<Key, Type>
1222
where
1323
Key: Default + Eq + Hash,
1424
Type: Default,
1525
{
26+
/// The root node of the Trie, which does not hold a value itself.
1627
root: Node<Key, Type>,
1728
}
1829

@@ -21,12 +32,21 @@ where
2132
Key: Default + Eq + Hash,
2233
Type: Default,
2334
{
35+
/// Creates a new, empty `Trie`.
36+
///
37+
/// # Returns
38+
/// A `Trie` instance with an empty root node.
2439
pub fn new() -> Self {
2540
Self {
2641
root: Node::default(),
2742
}
2843
}
2944

45+
/// Inserts a value into the Trie, associating it with a sequence of keys.
46+
///
47+
/// # Arguments
48+
/// - `key`: An iterable sequence of keys (e.g., characters in a string or integers in a vector).
49+
/// - `value`: The value to associate with the sequence of keys.
3050
pub fn insert(&mut self, key: impl IntoIterator<Item = Key>, value: Type)
3151
where
3252
Key: Eq + Hash,
@@ -38,60 +58,98 @@ where
3858
node.value = Some(value);
3959
}
4060

61+
/// Retrieves a reference to the value associated with a sequence of keys, if it exists.
62+
///
63+
/// # Arguments
64+
/// - `key`: An iterable sequence of keys (e.g., characters in a string or integers in a vector).
65+
///
66+
/// # Returns
67+
/// An `Option` containing a reference to the value if the sequence of keys exists in the Trie,
68+
/// or `None` if it does not.
4169
pub fn get(&self, key: impl IntoIterator<Item = Key>) -> Option<&Type>
4270
where
4371
Key: Eq + Hash,
4472
{
4573
let mut node = &self.root;
4674
for c in key {
47-
if node.children.contains_key(&c) {
48-
node = node.children.get(&c).unwrap()
49-
} else {
50-
return None;
51-
}
75+
node = node.children.get(&c)?;
5276
}
5377
node.value.as_ref()
5478
}
5579
}
5680

5781
#[cfg(test)]
5882
mod tests {
59-
6083
use super::*;
6184

6285
#[test]
63-
fn test_insertion() {
86+
fn test_insertion_and_retrieval_with_strings() {
6487
let mut trie = Trie::new();
65-
assert_eq!(trie.get("".chars()), None);
6688

6789
trie.insert("foo".chars(), 1);
90+
assert_eq!(trie.get("foo".chars()), Some(&1));
6891
trie.insert("foobar".chars(), 2);
92+
assert_eq!(trie.get("foobar".chars()), Some(&2));
93+
assert_eq!(trie.get("foo".chars()), Some(&1));
94+
trie.insert("bar".chars(), 3);
95+
assert_eq!(trie.get("bar".chars()), Some(&3));
96+
assert_eq!(trie.get("baz".chars()), None);
97+
assert_eq!(trie.get("foobarbaz".chars()), None);
98+
}
6999

100+
#[test]
101+
fn test_insertion_and_retrieval_with_integers() {
70102
let mut trie = Trie::new();
71-
assert_eq!(trie.get(vec![1, 2, 3]), None);
72103

73104
trie.insert(vec![1, 2, 3], 1);
74-
trie.insert(vec![3, 4, 5], 2);
105+
assert_eq!(trie.get(vec![1, 2, 3]), Some(&1));
106+
trie.insert(vec![1, 2, 3, 4, 5], 2);
107+
assert_eq!(trie.get(vec![1, 2, 3, 4, 5]), Some(&2));
108+
assert_eq!(trie.get(vec![1, 2, 3]), Some(&1));
109+
trie.insert(vec![10, 20, 30], 3);
110+
assert_eq!(trie.get(vec![10, 20, 30]), Some(&3));
111+
assert_eq!(trie.get(vec![4, 5, 6]), None);
112+
assert_eq!(trie.get(vec![1, 2, 3, 4, 5, 6]), None);
75113
}
76114

77115
#[test]
78-
fn test_get() {
116+
fn test_empty_trie() {
117+
let trie: Trie<char, i32> = Trie::new();
118+
119+
assert_eq!(trie.get("foo".chars()), None);
120+
assert_eq!(trie.get("".chars()), None);
121+
}
122+
123+
#[test]
124+
fn test_insert_empty_key() {
125+
let mut trie: Trie<char, i32> = Trie::new();
126+
127+
trie.insert("".chars(), 42);
128+
assert_eq!(trie.get("".chars()), Some(&42));
129+
assert_eq!(trie.get("foo".chars()), None);
130+
}
131+
132+
#[test]
133+
fn test_overlapping_keys() {
79134
let mut trie = Trie::new();
80-
trie.insert("foo".chars(), 1);
81-
trie.insert("foobar".chars(), 2);
82-
trie.insert("bar".chars(), 3);
83-
trie.insert("baz".chars(), 4);
84135

85-
assert_eq!(trie.get("foo".chars()), Some(&1));
86-
assert_eq!(trie.get("food".chars()), None);
136+
trie.insert("car".chars(), 1);
137+
trie.insert("cart".chars(), 2);
138+
trie.insert("carter".chars(), 3);
139+
assert_eq!(trie.get("car".chars()), Some(&1));
140+
assert_eq!(trie.get("cart".chars()), Some(&2));
141+
assert_eq!(trie.get("carter".chars()), Some(&3));
142+
assert_eq!(trie.get("care".chars()), None);
143+
}
87144

145+
#[test]
146+
fn test_partial_match() {
88147
let mut trie = Trie::new();
89-
trie.insert(vec![1, 2, 3, 4], 1);
90-
trie.insert(vec![42], 2);
91-
trie.insert(vec![42, 6, 1000], 3);
92-
trie.insert(vec![1, 2, 4, 16, 32], 4);
93148

94-
assert_eq!(trie.get(vec![42, 6, 1000]), Some(&3));
95-
assert_eq!(trie.get(vec![43, 44, 45]), None);
149+
trie.insert("apple".chars(), 10);
150+
assert_eq!(trie.get("app".chars()), None);
151+
assert_eq!(trie.get("appl".chars()), None);
152+
assert_eq!(trie.get("apple".chars()), Some(&10));
153+
assert_eq!(trie.get("applepie".chars()), None);
96154
}
97155
}

src/string/pangram.rs

Lines changed: 60 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,53 @@
1+
//! This module provides functionality to check if a given string is a pangram.
2+
//!
3+
//! A pangram is a sentence that contains every letter of the alphabet at least once.
4+
//! This module can distinguish between a non-pangram, a regular pangram, and a
5+
//! perfect pangram, where each letter appears exactly once.
6+
17
use std::collections::HashSet;
28

9+
/// Represents the status of a string in relation to the pangram classification.
310
#[derive(PartialEq, Debug)]
411
pub enum PangramStatus {
512
NotPangram,
613
Pangram,
714
PerfectPangram,
815
}
916

10-
/// Function that checks if the slice is a pangram
11-
///
12-
/// ## Arguments
13-
///
14-
/// * `pangram_str` - the slice that will be checked if is a pangram
15-
///
16-
/// ## Examples
17+
fn compute_letter_counts(pangram_str: &str) -> std::collections::HashMap<char, usize> {
18+
let mut letter_counts = std::collections::HashMap::new();
19+
20+
for ch in pangram_str
21+
.to_lowercase()
22+
.chars()
23+
.filter(|c| c.is_ascii_alphabetic())
24+
{
25+
*letter_counts.entry(ch).or_insert(0) += 1;
26+
}
27+
28+
letter_counts
29+
}
30+
31+
/// Determines if the input string is a pangram, and classifies it as either a regular or perfect pangram.
1732
///
18-
/// ```
19-
/// use the_algorithms_rust::string::is_pangram;
20-
/// use std::collections::HashSet;
21-
/// use the_algorithms_rust::string::PangramStatus;
33+
/// # Arguments
2234
///
23-
/// assert_eq!(
24-
/// is_pangram("This is not a pangram"),
25-
/// PangramStatus::NotPangram
26-
/// );
35+
/// * `pangram_str` - A reference to the string slice to be checked for pangram status.
2736
///
28-
/// assert_eq!(
29-
/// is_pangram("The quick brown fox jumps over the lazy dog"),
30-
/// PangramStatus::Pangram
31-
/// );
37+
/// # Returns
3238
///
33-
/// assert_eq!(
34-
/// is_pangram("Mr. Jock, TV quiz PhD, bags few lynx"),
35-
/// PangramStatus::PerfectPangram
36-
/// );
37-
/// ```
39+
/// A `PangramStatus` enum indicating whether the string is a pangram, and if so, whether it is a perfect pangram.
3840
pub fn is_pangram(pangram_str: &str) -> PangramStatus {
39-
let alphabet: HashSet<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
41+
let letter_counts = compute_letter_counts(pangram_str);
4042

41-
let letters_used: HashSet<char> = pangram_str
42-
.to_lowercase()
43-
.chars()
44-
.filter(|c| c.is_ascii_alphabetic())
45-
.collect();
43+
let alphabet: HashSet<char> = ('a'..='z').collect();
44+
let used_letters: HashSet<_> = letter_counts.keys().cloned().collect();
4645

47-
if letters_used != alphabet {
46+
if used_letters != alphabet {
4847
return PangramStatus::NotPangram;
49-
};
48+
}
5049

51-
if pangram_str.chars().filter(|c| c.is_alphabetic()).count() == alphabet.len() {
50+
if letter_counts.values().all(|&count| count == 1) {
5251
PangramStatus::PerfectPangram
5352
} else {
5453
PangramStatus::Pangram
@@ -59,46 +58,35 @@ pub fn is_pangram(pangram_str: &str) -> PangramStatus {
5958
mod tests {
6059
use super::*;
6160

62-
#[test]
63-
fn test_not_pangram() {
64-
assert_eq!(
65-
is_pangram("This is not a pangram"),
66-
PangramStatus::NotPangram
67-
);
68-
assert_eq!(is_pangram("today is a good day"), PangramStatus::NotPangram);
69-
assert_eq!(
70-
is_pangram(
71-
"this is almost a pangram but it does not have bcfghjkqwxy and the last letter"
72-
),
73-
PangramStatus::NotPangram
74-
);
75-
}
76-
77-
#[test]
78-
fn test_pangram() {
79-
assert_eq!(
80-
is_pangram("The quick brown fox jumps over the lazy dog"),
81-
PangramStatus::Pangram
82-
);
83-
assert_eq!(
84-
is_pangram("A mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent"),
85-
PangramStatus::Pangram
86-
);
87-
assert_eq!(
88-
is_pangram("Amazingly few discotheques provide jukeboxes"),
89-
PangramStatus::Pangram
90-
);
91-
assert_eq!(
92-
is_pangram("How vexingly quick daft zebras jump"),
93-
PangramStatus::Pangram
94-
);
61+
macro_rules! pangram_tests {
62+
($($name:ident: $tc:expr,)*) => {
63+
$(
64+
#[test]
65+
fn $name() {
66+
let (input, expected) = $tc;
67+
assert_eq!(is_pangram(input), expected);
68+
}
69+
)*
70+
};
9571
}
9672

97-
#[test]
98-
fn test_perfect_pangram() {
99-
assert_eq!(
100-
is_pangram("Mr. Jock, TV quiz PhD, bags few lynx"),
101-
PangramStatus::PerfectPangram
102-
);
73+
pangram_tests! {
74+
test_not_pangram_simple: ("This is not a pangram", PangramStatus::NotPangram),
75+
test_not_pangram_day: ("today is a good day", PangramStatus::NotPangram),
76+
test_not_pangram_almost: ("this is almost a pangram but it does not have bcfghjkqwxy and the last letter", PangramStatus::NotPangram),
77+
test_pangram_standard: ("The quick brown fox jumps over the lazy dog", PangramStatus::Pangram),
78+
test_pangram_boxer: ("A mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent", PangramStatus::Pangram),
79+
test_pangram_discotheques: ("Amazingly few discotheques provide jukeboxes", PangramStatus::Pangram),
80+
test_pangram_zebras: ("How vexingly quick daft zebras jump", PangramStatus::Pangram),
81+
test_perfect_pangram_jock: ("Mr. Jock, TV quiz PhD, bags few lynx", PangramStatus::PerfectPangram),
82+
test_empty_string: ("", PangramStatus::NotPangram),
83+
test_repeated_letter: ("aaaaa", PangramStatus::NotPangram),
84+
test_non_alphabetic: ("12345!@#$%", PangramStatus::NotPangram),
85+
test_mixed_case_pangram: ("ThE QuiCk BroWn FoX JumPs OveR tHe LaZy DoG", PangramStatus::Pangram),
86+
test_perfect_pangram_with_symbols: ("Mr. Jock, TV quiz PhD, bags few lynx!", PangramStatus::PerfectPangram),
87+
test_long_non_pangram: (&"a".repeat(1000), PangramStatus::NotPangram),
88+
test_near_pangram_missing_one_letter: ("The quick brown fox jumps over the lazy do", PangramStatus::NotPangram),
89+
test_near_pangram_missing_two_letters: ("The quick brwn f jumps ver the lazy dg", PangramStatus::NotPangram),
90+
test_near_pangram_with_special_characters: ("Th3 qu!ck brown f0x jumps 0v3r th3 l@zy d0g.", PangramStatus::NotPangram),
10391
}
10492
}

0 commit comments

Comments
 (0)